API & Routing
Define clear, predictable, and RESTful (or RPC-appropriate) API routes with consistent naming and structure.
A well-designed routing and API convention improves maintainability, security, versioning, and onboarding — making both frontend and backend code easier to navigate and integrate with.
Use consistent HTTP methods
Stick to standard HTTP method semantics:
GET
— fetch dataPOST
— create new resourcesPUT/PATCH
— update existingDELETE
— remove resource
Avoid mixing purposes (e.g., modifying state in a GET request).
GET /activate-user?id=42
POST /users/42/activate
Use nested routes for hierarchical resources
When a resource is logically nested within another (e.g., comments under a post), reflect that structure in the route. This improves clarity and aligns with data relationships.
1GET /posts/42/comments
2POST /posts/42/comments
Version your APIs explicitly
Always include a version in your API routes to ensure backward compatibility. This allows you to introduce changes over time without breaking existing clients.
GET /api/v1/users
Use URI versioning (/v1/
) as the most common approach. Other strategies (e.g., headers) exist, but are less explicit and harder to manage.
Use standard HTTP status codes
Always respond with appropriate HTTP status codes to indicate the outcome of a request. This improves interoperability, simplifies debugging, and aligns with client expectations.
Success: 2xx
Client Error: 4xx
Server Error: 5xx
1// Always returns 200 OK
2{ "error": "User not found" }
1404 Not Found
2{ "error": "User not found" }
Return a consistent error structure
Use a standard shape for error responses throughout your API. Include fields like message
, code
, and optionally details
for debugging or UI handling.
1{
2 "error": {
3 "message": "Invalid input",
4 "code": "VALIDATION_ERROR",
5 "details": [
6 { "field": "email", "message": "Must be a valid email" }
7 ]
8 }
9}
Support pagination, filtering and sorting
For endpoints that return collections, always support query parameters for pagination (limit
, offset
or page
), filtering (?status=active) and sorting (
sortBy
, order
). This improves performance and usability, especially for large datasets.
GET /users?limit=10&offset=20&status=active&sortBy=createdAt&order=desc
Use IDs and query params correctly
Use path parameters for identifying specific resources (/users/123
) and query parameters for optional filters, search terms, or UI controls. Avoid overloading query parameters for routing logic or resource identification.
GET /user?id=123
GET /users/123
Use action routes for non-CRUD behavior
When an operation doesn’t fit cleanly into a standard CRUD model (e.g., resetting a password, publishing a post), use action sub-routes to clarify intent.
1POST /users/42/reset-password
2POST /posts/123/publish
Avoid verbs in base URLs — use them only as nested actions within the resource context.
Ensure idempotency where needed
For critical operations (e.g., payments, state transitions), make them idempotent — calling them multiple times should have the same effect as calling once. This avoids double-processing due to retries or network issues.
A safe POST /orders
with Idempotency-Key
header:
1POST /orders
2Idempotency-Key: x-unique-key-123
Respect rate limiting and throttling
Implement rate limits for public APIs to protect your backend and ensure fair usage. Respond with 429 Too Many Requests
and include headers like Retry-After
to guide clients.
1HTTP/1.1 429 Too Many Requests
2Retry-After: 60
Document your API with OpenAPI or similar
Document your API using an open standard like OpenAPI (Swagger). This ensures consumers have clear, structured, and always-up-to-date reference for every route, method, parameter, and response shape.