REST API Design Principles: Building Intuitive APIs
Good API design reduces friction for consumers and speeds integration.
Resource Naming
# Bad: verb-based URLs
GET /getUsers
POST /createUser
PUT /updateUser/1
GET /deleteUser/1
# Good: noun-based resources with HTTP verbs
GET /users List users
POST /users Create user
GET /users/1 Get user
PUT /users/1 Replace user
PATCH /users/1 Partial update
DELETE /users/1 Delete user
# Nested resources for relationships
GET /users/1/orders User's orders
POST /users/1/orders Create order for user
GET /users/1/orders/5 Specific order
HTTP Status Codes
2xx Success:
200 OK - GET/PUT/PATCH success
201 Created - POST creates resource
204 No Content - DELETE success (no body)
4xx Client Errors:
400 Bad Request - Invalid input/malformed request
401 Unauthorized - Not authenticated
403 Forbidden - Authenticated but not authorized
404 Not Found - Resource doesn't exist
409 Conflict - Duplicate resource
422 Unprocessable Entity - Validation failed
5xx Server Errors:
500 Internal Server Error - Unexpected server failure
503 Service Unavailable - Temporary downtime
Error Response Format
{
"error": {
"code": "VALIDATION_FAILED",
"message": "Request validation failed",
"details": [
{
"field": "email",
"code": "INVALID_FORMAT",
"message": "Email must be a valid email address"
},
{
"field": "age",
"code": "MIN_VALUE",
"message": "Age must be at least 18"
}
],
"requestId": "req_abc123",
"timestamp": "2025-01-01T00:00:00Z"
}
}
Filtering, Sorting, Pagination
# Filtering
GET /users?status=active&role=admin
# Sorting
GET /users?sort=createdAt:desc,name:asc
# Pagination (cursor-based - preferred for large datasets)
GET /users?limit=20&cursor=eyJpZCI6MTB9
# Pagination (offset-based - simpler)
GET /users?limit=20&offset=40
# Field selection (sparse fieldsets)
GET /users?fields=id,name,email
Pagination Response
{
"data": [...],
"pagination": {
"total": 1000,
"limit": 20,
"offset": 40,
"nextCursor": "eyJpZCI6NjB9",
"hasMore": true
}
}
Versioning
# URL versioning (most common)
GET /v1/users
GET /v2/users
# Header versioning
GET /users
Accept: application/vnd.myapi.v2+json
# Query param (avoid for REST)
GET /users?version=2
Idempotency
# Idempotency keys for safe retries
POST /payments
Idempotency-Key: pay_unique_client_key_123
# Server stores result and returns same response on duplicate
HATEOAS Links
{
"id": 1,
"name": "Alice",
"links": {
"self": "/users/1",
"orders": "/users/1/orders",
"profile": "/users/1/profile"
}
}
Consistent, well-designed APIs reduce integration time from weeks to hours.