REST API Design
Updated June 3, 2026The Anatomy of a Great REST API
REST (Representational State Transfer) was defined in 2000 by Roy Fielding. Despite newer alternatives like GraphQL and gRPC, it remains the default for public-facing web services.
A well-designed REST API is self-describing. Developers can guess how it works without reading the documentation.
CRUD operations — GET/POST/PUT/PATCH/DELETE on /users and /users/:id
Nouns, Not Verbs
In REST, your URLs (endpoints) should represent Resources (things), not actions. You let the HTTP methods (GET, POST, PUT, DELETE) define the action.
Prefer /users over /getAllUsers, /createUser, or /deleteUser?id=5. The URL names the resource; the HTTP method names the operation.
Which URL design follows REST conventions for deleting a specific user?
The Standard CRUD Mapping:
- GET
/users— Read all users - POST
/users— Create a new user - GET
/users/123— Read user #123 - PUT
/users/123— Completely replace user #123 - PATCH
/users/123— Partially update user #123 (e.g., just change their email) - DELETE
/users/123— Delete user #123
Nested resource paths — /users/123/orders and cursor-based pagination
Nesting for Relationships
If resources are related, your URLs should reflect that hierarchy. Keep it logical, but don't nest too deeply. Cap it at two levels. /users/123/orders and /users/123/orders/456 are fine. /users/123/orders/456/items/789/reviews is not. Once you go that deep, reference the inner resource directly with /items/789/reviews.
What is the correct HTTP status code to return after a successful POST request that creates a new resource?
Play by the HTTP Status Code Rules
HTTP has built-in status codes. Don't reinvent the wheel by returning a 200 OK with an error message buried in the JSON payload. Use the standards:
Success:
200 OK: Standard success.201 Created: Used for successful POST requests.
Client Errors (The frontend messed up):
400 Bad Request: Invalid input (e.g., malformed JSON or missing required fields).401 Unauthorized: The user isn't logged in / bad API key.403 Forbidden: Logged in, but trying to access something they don't own.404 Not Found: Resource doesn't exist.429 Too Many Requests: Rate limiting kicks in.
Server Errors (The backend messed up):
500 Internal Server Error: The backend crashed.503 Service Unavailable: Servers are down or overloaded.
A client sends a request with a valid token but tries to access another user's private data. What status code should the server return?
Returning a 200 OK response with an error message inside the JSON body is an acceptable REST practice.
Filtering, Sorting, and Pagination
When a client hits GET /users, you don't want to return 10 million rows from your database. Use query parameters to tame lists: filtering via /users?role=admin&status=active, sorting via /users?sort=-createdAt (a leading minus conventionally means descending), and pagination via /users?limit=20&offset=40 to skip the first 40 rows and return the next 20.
Pro tip: For massive datasets, use cursor-based pagination (
/users?after=cursor_xyz) instead of offsets. Offsets degrade significantly as you page deeper into large tables.
Why is cursor-based pagination preferred over offset-based pagination for large datasets?
Versioning
APIs evolve. If you change the shape of your JSON payload, you might break thousands of clients that rely on the old format.
Version your API from day one. The most common approach is embedding it in the URL:
https://api.stripe.com/v1/customers
Alternatively, companies like Stripe use headers to specify API versions based on dates (e.g., Stripe-Version: 2023-10-16), keeping the URL clean.
REST APIs should be versioned from day one, even if no breaking changes are planned yet.
Summary
URLs map to resources (use /articles, not /getArticles) and HTTP methods map to actions (GET, POST, PUT, DELETE). Show relationships in the URL hierarchy (e.g., /authors/12/articles) but avoid nesting beyond two levels. Use HTTP status codes correctly: 400 for bad client input, 404 for missing resources, 500 for backend failures. Control list responses with ?limit=10&sort=asc query parameters, and prefer cursor-based pagination for large datasets. Always version your API from the start (e.g., /v1/) so you can evolve the contract without breaking existing clients. Stripe and Twilio are the real-world gold standards worth studying.
How helpful was this content?
Comments
Sign in to join the discussion
Saved on this device only
Sign in to sync progress across devices