What is REST?
REST, or Representational State Transfer, is an architectural style for designing HTTP APIs. Instead of inventing commands, you model your domain as resources and use the methods HTTP already defines to act on them. A POST /posts creates a post, GET /posts/42 reads one, PATCH /posts/42 updates it and DELETE /posts/42 removes it.
The value is familiarity. Every HTTP client, proxy, cache and tool already understands methods, status codes and headers. When you follow the conventions, clients can predict how your API behaves without reading a bespoke manual.
Resources and URIs
Resources are the nouns of your API. Use plural collections and identify individual items by id.
GET /posts
POST /posts
GET /posts/42
PUT /posts/42
PATCH /posts/42
DELETE /posts/42
- Use nouns, not verbs. The method is the verb.
- Use plural collection names consistently.
- Keep URLs lowercase with hyphens, not underscores or camelCase.
- Nest only when the relationship is essential, like
/posts/42/comments. - Do not put the format in the path; use the
Acceptheader.
Deeper nesting gets awkward fast. /users/1/posts/2/comments/3 is hard to build and document; prefer /comments/3 and let clients filter.
Methods and their semantics
Each method has defined semantics that clients and infrastructure rely on.
| Method | Purpose | Safe | Idempotent |
|---|---|---|---|
| GET | Read a resource or collection | Yes | Yes |
| POST | Create a resource or trigger an action | No | No |
| PUT | Replace a resource | No | Yes |
| PATCH | Partially update a resource | No | No |
| DELETE | Remove a resource | No | Yes |
Safe means it does not change state; idempotent means repeating it has the same effect as doing it once. Never change data with GET, because clients, crawlers and caches may issue GET requests freely.
Status codes
Return the code that matches what happened. This is the part clients depend on most.
- 200 OK — success with a body.
- 201 Created — a resource was created; include a
Locationheader. - 204 No Content — success with no body, common for DELETE.
- 400 Bad Request — malformed request.
- 401 Unauthorized — authentication missing or invalid.
- 403 Forbidden — authenticated but not allowed.
- 404 Not Found — no such resource.
- 409 Conflict — state conflict, such as a duplicate.
- 422 Unprocessable Entity — syntactically valid but fails validation.
- 429 Too Many Requests — rate limited; include
Retry-After. - 500 Internal Server Error — unexpected server failure.
Returning 200 with { "success": false } hides failures from clients, caches and monitoring, and forces every client to invent its own error handling.
Collections: filtering, sorting and pagination
Collections need a consistent query language.
GET /posts?status=published&sort=-createdAt&limit=20&cursor=abc123
- Filtering with
field=value, and repeated keys for OR. - Sorting with
sort=fieldorsort=-fieldfor descending. - Pagination with
limitandcursor(orpageandperPage). - Field selection with
fields=id,titlewhen clients want less. - Search with a dedicated
qparameter.
Prefer cursor pagination for large or changing datasets, and always return the next cursor in the response so clients can page without guessing.
{
"data": [{ "id": "42", "title": "Hello" }],
"pagination": { "nextCursor": "abc123", "hasMore": true }
}
Representations and responses
Responses are representations of resources. JSON is the norm, with a stable shape.
{
"id": "42",
"title": "Hello",
"createdAt": "2026-09-15T10:00:00Z"
}
- Use camelCase or snake_case consistently, not both.
- Use ISO 8601 strings for dates.
- Use stable ids as strings when clients might exceed number precision.
- Envelope collections with
dataandpagination, but return single resources directly. - Support
Acceptand setContent-Typecorrectly.
Statelessness and caching
Each request should carry everything needed to process it: the URL, method, headers and body. Do not rely on server memory between requests, which is what lets you run many instances behind a load balancer.
Because GET is safe and responses are self-contained, REST works well with HTTP caching. Set Cache-Control and validators such as ETag on cacheable resources, as covered in the HTTP guide.
Errors
Use one error shape everywhere and document it.
{
"error": {
"code": "validation_error",
"message": "Title is required",
"details": [{ "field": "title", "issue": "required" }]
}
}
A machine-readable code lets clients branch, a message is safe to show users, and details helps forms highlight fields. Pair it with the right status code.
Best practices
- Model resources with nouns and let methods express actions.
- Return the most specific status code you can.
- Version the API before you need to, and document it (see API Versioning).
- Paginate every collection and cap
limit. - Validate input and return structured errors.
- Use consistent naming and casing throughout.
- Cache safe GET responses with explicit headers.
- Rate limit and authenticate (see Rate Limiting).
Common mistakes
- Verb-based URLs that duplicate HTTP methods.
- Returning 200 for errors.
- Unbounded collections with no pagination.
- Inconsistent casing, date formats or error shapes.
- Deep nesting that becomes impossible to maintain.
- Mutating state on GET.
- Breaking clients by changing response shapes silently.
Where to go next
REST is the default way to expose a backend. Ground it in the HTTP guide, document it with OpenAPI, evolve it safely with API Versioning, and protect it with Rate Limiting. Compare the model with GraphQL when clients need more flexibility.