REST API Design Best Practices
Practical conventions for resource naming, versioning, error handling, and pagination that make an API pleasant to integrate against — and keep it stable as it grows.
NavikaTech
Updated January 5, 2026
Most REST API design debates are really consistency debates. Any reasonable convention beats an inconsistent mix of conventions, because the cost of a REST API isn't in any single endpoint — it's in the cognitive overhead an integrator pays every time the pattern changes from one endpoint to the next.
Resources are nouns, methods are verbs
| Action | Bad | Good |
|---|---|---|
| List orders | GET /getOrders | GET /orders |
| Create order | POST /createOrder | POST /orders |
| Get one order | GET /order?id=123 | GET /orders/123 |
| Cancel an order | POST /orders/123/doCancel | POST /orders/123/cancel or PATCH /orders/123 |
Version from day one
It's tempting to skip versioning for a v1 API — 'we'll add it when we need v2.' The problem is that by the time you need v2, every existing client is already depending on unversioned URLs, and introducing versioning becomes a breaking change in itself. Bake /v1/ into the URL structure even if no v2 ever ships.
// Version in the URL path — visible, cacheable, and easy for
// integrators to pin to in their own client code.
app.use("/v1/orders", ordersRouterV1);
app.use("/v1/users", usersRouterV1);
// Alternative: header-based versioning (Accept: application/vnd.api+json;version=1)
// is more "correct" per REST purists but meaningfully harder for most
// integrators to discover and use correctly. Path versioning wins on
// pragmatism for the majority of public APIs.A consistent error envelope
Nothing frustrates API integrators faster than every endpoint returning errors in a slightly different shape. Standardize on one envelope, loosely modeled on RFC 7807, and use it everywhere without exception.
{
"error": {
"code": "VALIDATION_ERROR",
"message": "The 'email' field must be a valid email address.",
"field": "email",
"requestId": "req_8f2a1c9d"
}
}Pagination: cursor-based over offset
Offset pagination (?page=2&limit=20) is simple but breaks under concurrent writes — insert a row while a client is paginating and they'll see duplicates or skip records entirely. Cursor-based pagination is more robust for any dataset that changes while being read.
interface PaginatedResponse<T> {
data: T[];
nextCursor: string | null;
}
app.get("/v1/orders", async (req, res) => {
const limit = Math.min(Number(req.query.limit) || 20, 100);
const cursor = req.query.cursor as string | undefined;
const orders = await db.order.findMany({
take: limit + 1, // fetch one extra to know if there's a next page
...(cursor && { cursor: { id: cursor }, skip: 1 }),
orderBy: { id: "asc" },
});
const hasMore = orders.length > limit;
const data = hasMore ? orders.slice(0, -1) : orders;
const response: PaginatedResponse<Order> = {
data,
nextCursor: hasMore ? data[data.length - 1].id : null,
};
res.json(response);
});A REST API's quality is measured by how few support tickets it generates, not by how closely it follows the spec. — Arjun Mehta, Principal Software Engineer, NavikaTech
Conclusion
Consistent naming, day-one versioning, a single error envelope, and cursor-based pagination cover the majority of what makes a REST API pleasant to integrate against. None of these are exotic — the discipline is applying them uniformly across every endpoint, forever, even under deadline pressure.
Key Takeaways
- Model resources as nouns and use HTTP methods as verbs consistently — this alone eliminates most API design debates.
- Version from day one, even if v1 is the only version that ever ships — retrofitting versioning later breaks every existing client.
- A consistent error envelope across every endpoint saves integrators significant debugging time.
- Cursor-based pagination scales better than offset pagination for any dataset that changes while being paginated.
Recommended Resources
NavikaTech
NavikaTech shares practical engineering notes on web development, app development, custom software, automation, AI, and long-term software maintenance.
Related Articles
Stay Updated With Modern Software Engineering
One email, occasionally — practical engineering writing from the NavikaTech team, no noise.