REST API Design
REST API Design Resource Naming & HTTP Methods # Use nouns (not verbs), plural, lowercase, hyphens GET /users # list users POST /users # create user GET /users/…
REST API Design
Resource Naming & HTTP Methods
# Use nouns (not verbs), plural, lowercase, hyphens
GET /users # list users
POST /users # create user
GET /users/{id} # get user
PUT /users/{id} # replace user (full update)
PATCH /users/{id} # partial update
DELETE /users/{id} # delete user
# Nested resources
GET /users/{id}/posts # user's posts
POST /users/{id}/posts # create post for user
GET /users/{id}/posts/{postId} # specific post
# Actions that don't fit CRUD (use nouns when possible)
POST /orders/{id}/cancel # good
POST /orders/{id}/cancellations # RESTful noun variant
GET /search?q=query&type=user # search
# Don't use verbs in paths
# BAD: POST /createUser, GET /getUser, POST /users/delete/{id}
# GOOD: POST /users, GET /users/{id}, DELETE /users/{id}HTTP Status Codes
# 2xx — Success
200 OK — GET, PUT, PATCH responses
201 Created — POST response; include Location: /users/{newId} header
204 No Content — DELETE, or PATCH with no body response
# 3xx — Redirection
301 Moved Permanently — permanent URL change
302 Found / 307 Temp — temporary redirect
304 Not Modified — ETag/If-None-Match cache hit
# 4xx — Client errors
400 Bad Request — malformed request, validation failure
401 Unauthorized — not authenticated (missing/invalid token)
403 Forbidden — authenticated but not authorized
404 Not Found — resource doesn't exist
405 Method Not Allowed — wrong HTTP method for this endpoint
409 Conflict — duplicate (unique constraint), optimistic lock conflict
410 Gone — permanently deleted (vs 404 = never existed)
422 Unprocessable — validation errors on valid JSON
429 Too Many Requests — rate limited; include Retry-After header
# 5xx — Server errors
500 Internal Server Error — unexpected error (never expose details)
502 Bad Gateway — upstream service failed
503 Service Unavailable — overloaded/down; include Retry-After headerResponse Format
// Success — single resource
{
"id": "usr_123",
"email": "alice@example.com",
"name": "Alice",
"role": "user",
"createdAt": "2024-01-15T10:30:00Z"
}
// Success — list with pagination
{
"data": [...],
"pagination": {
"page": 1,
"limit": 20,
"total": 143,
"totalPages": 8,
"hasNextPage": true,
"nextCursor": "eyJpZCI6MTIzfQ=="
}
}
// Error response — consistent shape
{
"error": {
"code": "VALIDATION_ERROR",
"message": "Request validation failed",
"details": [
{ "field": "email", "message": "Invalid email format" },
{ "field": "password", "message": "Must be at least 8 characters" }
],
"requestId": "req_abc123"
}
}Headers & Versioning
# Request headers
Content-Type: application/json
Accept: application/json
Authorization: Bearer <token>
X-Request-ID: client-generated-uuid
If-None-Match: "abc123" # conditional GET (ETag)
If-Match: "abc123" # optimistic concurrency check
# Response headers
Content-Type: application/json; charset=utf-8
X-Request-ID: req_abc123 # echo back for tracing
X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 999
X-RateLimit-Reset: 1700000000
ETag: "abc123" # for caching
Location: /users/123 # after 201 Created
Retry-After: 60 # after 429 or 503
# API Versioning strategies
# 1. URL path (most common, explicit)
GET /v1/users
GET /v2/users
# 2. Query parameter
GET /users?version=2
# 3. Header (cleanest, least visible)
Accept: application/vnd.api.v2+json
API-Version: 2
# Prefer URL versioning for public APIs — visible and cacheableFiltering, Sorting & Pagination
# Filtering
GET /users?role=admin&isActive=true
GET /users?age_gte=18&age_lte=65
GET /posts?status=published&tag=javascript
# Sorting
GET /users?sort=name # ascending
GET /users?sort=-createdAt # descending (minus prefix)
GET /users?sort=-createdAt,name # multiple fields
# Field selection (sparse fieldsets)
GET /users?fields=id,name,email
# Pagination — offset
GET /users?page=2&limit=20
# Pagination — cursor (better for large datasets)
GET /users?cursor=eyJpZCI6MTIzfQ==&limit=20
# Response includes nextCursor for subsequent request
# Search
GET /users?q=alice
GET /posts?q=javascript&in=title,body
# Include relations
GET /posts?include=author,tags