REST API Design: Make It Obvious
A good API is one that a developer can guess. If they need /users, GET /users should work. If they need user 42, GET /users/42 should work. When APIs follow consistent patterns, documentation becomes optional for common operations.
Resource Naming
| Do | Don't | Why |
|---|---|---|
GET /users | GET /getUsers | The HTTP method is the verb |
GET /users/42 | GET /user/42 | Collections are plural |
POST /users | POST /createUser | POST already means "create" |
GET /users/42/orders | GET /getUserOrders?userId=42 | Nested resources show relationships |
kebab-case in URLs | camelCase or snake_case | URLs are case-insensitive by convention |
HTTP Methods
GET /users → List users
GET /users/42 → Get user 42
POST /users → Create a user (body has data)
PUT /users/42 → Replace user 42 entirely
PATCH /users/42 → Update specific fields of user 42
DELETE /users/42 → Delete user 42Pagination
Offset-based (simple, most common):
GET /users?page=2&limit=20
{
"data": [...],
"meta": {
"page": 2,
"limit": 20,
"total": 156,
"total_pages": 8
}
}Cursor-based (better for large datasets, real-time data):
GET /users?cursor=eyJpZCI6NDJ9&limit=20
{
"data": [...],
"meta": {
"next_cursor": "eyJpZCI6NjJ9",
"has_more": true
}
}Error Responses
Be consistent. Pick a format and stick with it:
// Standard error format
{
"error": {
"code": "VALIDATION_ERROR",
"message": "Request validation failed",
"details": [
{
"field": "email",
"message": "Must be a valid email address"
},
{
"field": "age",
"message": "Must be at least 18"
}
]
}
}Always include: machine-readable error code, human-readable message, and field-level details for validation errors.
Versioning
| Strategy | Example | Pros/Cons |
|---|---|---|
| URL path | /api/v1/users | Simple, explicit, easy to route. Most common. |
| Header | Accept: application/vnd.api+json;version=1 | Clean URLs, harder to test in browser. |
| Query param | /users?version=1 | Easy to add, clutters query string. |
URL path versioning wins for simplicity. Version only when you have breaking changes.
Filtering, Sorting, and Fields
// Filtering
GET /users?role=admin&status=active
// Sorting
GET /users?sort=-created_at,name // - prefix = descending
// Sparse fields (return only what's needed)
GET /users?fields=id,name,email
// Search
GET /users?q=johnAuthentication
// Bearer token (most common for APIs)
GET /users HTTP/1.1
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...
// API key (for server-to-server)
GET /users HTTP/1.1
X-API-Key: sk_live_abc123API reference tool: HTTP Status Codes — pick the right status code for every response.