REST API design
Resource naming
- Plural nouns for collections:
/users,/orders. - Singular for individual items via id:
/users/42. - Sub-resources with hierarchy:
/users/42/orders. - Avoid verbs in URLs (
/users/42/activate→POST /users/42/activations). - Use
kebab-case, notsnake_case, in path segments.
HTTP methods
| Method | Idempotent? | Use for |
|---|---|---|
| GET | ✅ | read |
| POST | ❌ | create / non-idempotent action |
| PUT | ✅ | full replace |
| PATCH | ❌ | partial update |
| DELETE | ✅ | remove |
Status codes
200OK — success with response body201Created — POST that created a resource (includeLocation:header)204No Content — success, no body (DELETE)400Bad Request — client malformed request401Unauthorized — no/invalid auth403Forbidden — authenticated but not allowed404Not Found — resource doesn't exist409Conflict — version mismatch / duplicate422Unprocessable Entity — semantically invalid (validation)429Too Many Requests — rate limited500/502/503— server errors
Don't return 200 {error: ...} — clients can't tell success from failure.
Pagination
Cursor-based is best for streams:
{
"data": [...],
"next_cursor": "eyJpZCI6MTAwMH0="
}
Offset-based for lists with stable sort:
GET /orders?limit=50&offset=100
Error shape
Pick ONE shape and use it everywhere:
{
"error": {
"code": "VALIDATION_FAILED",
"message": "email is required",
"field": "email",
"trace_id": "..."
}
}
Versioning
- URL path (
/v1/) — explicit, easy. - Header (
Accept: application/vnd.app.v1+json) — clean URLs, harder to debug. - Pick one. Don't mix.
Anti-patterns
GET /users/delete/42— mutating with GET is illegal in caches and middleboxes.- Returning HTML on errors — clients expect JSON they can parse.
- Different shapes for the same resource on
GETvsPOSTresponse. - Skipping rate limits "until it's a problem" (it'll be a problem at 3am).
Source: ManasaEdavalli-TharunSure/opengriffin — distributed by TomeVault.