REST API Design Principles and Best Practices
Well-designed APIs are crucial for building scalable applications. Here's how to design REST APIs that developers love.
URL Structure
Use Nouns, Not Verbs
# Bad
GET /getUsers
POST /createUser
DELETE /deleteUser/1
# Good
GET /users
POST /users
DELETE /users/1Use Plural Nouns
# Consistent plural naming
GET /users
GET /users/123
GET /posts
GET /posts/456/commentsHierarchical Resources
# Nested resources
GET /users/123/posts # User's posts
GET /posts/456/comments # Post's comments
GET /users/123/posts/789 # Specific post by userHTTP Methods
# CRUD Operations
GET /users # List all users
GET /users/123 # Get single user
POST /users # Create user
PUT /users/123 # Update entire user
PATCH /users/123 # Partial update
DELETE /users/123 # Delete userStatus Codes
Success Codes
200 OK # Successful GET, PUT, PATCH
201 Created # Successful POST (resource created)
204 No Content # Successful DELETEClient Error Codes
400 Bad Request # Invalid request body
401 Unauthorized # Authentication required
403 Forbidden # Not enough permissions
404 Not Found # Resource doesn't exist
409 Conflict # Resource conflict
422 Unprocessable # Validation error
429 Too Many Req # Rate limitedServer Error Codes
500 Internal Error # Server error
502 Bad Gateway # Upstream error
503 Unavailable # Service downRequest & Response
Request Body (Create)
POST /users
Content-Type: application/json
{
"name": "John Doe",
"email": "john@example.com",
"role": "user"
}Response (Success)
HTTP/1.1 201 Created
Content-Type: application/json
{
"data": {
"id": "123",
"name": "John Doe",
"email": "john@example.com",
"role": "user",
"createdAt": "2024-04-05T10:30:00Z"
}
}Response (Error)
HTTP/1.1 422 Unprocessable Entity
Content-Type: application/json
{
"error": {
"code": "VALIDATION_ERROR",
"message": "Validation failed",
"details": [
{
"field": "email",
"message": "Invalid email format"
}
]
}
}Pagination
Offset-Based
GET /users?page=2&limit=20
Response:
{
"data": [...],
"pagination": {
"page": 2,
"limit": 20,
"total": 150,
"totalPages": 8
}
}Cursor-Based (Better for Large Datasets)
GET /users?cursor=eyJpZCI6MTIzfQ&limit=20
Response:
{
"data": [...],
"pagination": {
"nextCursor": "eyJpZCI6MTQzfQ",
"hasMore": true
}
}Filtering & Sorting
Query Parameters
# Filtering
GET /users?role=admin&status=active
# Sorting
GET /users?sort=createdAt&order=desc
# Multiple sorts
GET /users?sort=-createdAt,name
# Field selection
GET /users?fields=id,name,email
# Search
GET /users?search=johnVersioning
URL Path (Recommended)
GET /v1/users
GET /v2/usersHeader
GET /users
Accept: application/vnd.api+json; version=1Authentication
Bearer Token
GET /users
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...API Key
GET /users
X-API-Key: your-api-key-hereRate Limiting Headers
HTTP/1.1 200 OK
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 95
X-RateLimit-Reset: 1712345678HATEOAS (Optional)
{
"data": {
"id": "123",
"name": "John Doe"
},
"links": {
"self": "/users/123",
"posts": "/users/123/posts",
"followers": "/users/123/followers"
}
}Best Practices Summary
- Use HTTPS - Always encrypt API traffic
- Version your API - Plan for breaking changes
- Use proper status codes - Be specific about errors
- Validate input - Never trust client data
- Paginate lists - Prevent large responses
- Document everything - Use OpenAPI/Swagger
- Rate limit - Protect your API
- Use consistent naming - camelCase or snake_case
- Include timestamps - createdAt, updatedAt
- Support partial updates - Use PATCH wisely
A well-designed API is intuitive, consistent, and a pleasure to work with. Invest time in design before implementation.