REST API Design Best Practices: A Complete Guide
REST (Representational State Transfer) APIs are the backbone of modern web services. This comprehensive guide covers everything you need to design scalable, maintainable, and developer-friendly REST APIs.
What is REST?
REST is an architectural style for designing networked applications. It relies on a stateless, client-server communication protocolโalmost always HTTP.
Core Principles
- Client-Server Architecture: Separation of concerns between client and server
- Statelessness: Each request contains all information needed
- Cacheability: Responses can be cached for performance
- Uniform Interface: Consistent resource identification
- Layered System: Scalable architecture through intermediaries
Resource Naming Conventions
The foundation of good REST API design is proper resource naming. Resources should be nouns, not verbs.
Good vs Bad Examples
# Good - Nouns representing resources
GET /users
GET /users/123
GET /users/123/orders
POST /orders
# Bad - Verbs or unclear naming
GET /getUsers
GET /userInfo
POST /createOrder
GET /fetchUserData/123
Resource Naming Rules
# Use plural nouns for collections
GET /products # Collection of products
GET /products/456 # Single product
# Use lowercase with hyphens for multi-word resources
GET /user-profiles
GET /order-items
# Use nesting to show relationships
GET /users/123/orders # User's orders
GET /orders/789/items # Order's items
GET /users/123/orders/456 # Specific order
# Avoid deep nesting (max 2-3 levels)
# Good
GET /users/123/orders
# Too deep - consider flattening
GET /orders?userId=123
Concrete Examples
# Blog API example
GET /posts # List all posts
GET /posts/123 # Single post
GET /posts/123/comments # Comments on a post
POST /posts/123/comments # Add comment to post
GET /authors # List all authors
GET /authors/456/posts # Author's posts
# E-commerce API example
GET /products # List products
GET /products?category=books # Filter by category
GET /products?sort=price_asc # Sort products
GET /products?page=2&limit=20 # Pagination
POST /carts # Create cart
PUT /carts/123/items # Update cart
DELETE /carts/123/items/456 # Remove item
HTTP Methods
Use HTTP methods semantically to indicate action:
| Method | Purpose | Idempotent |
|---|---|---|
| GET | Retrieve resources | Yes |
| POST | Create new resources | No |
| PUT | Replace entire resource | Yes |
| PATCH | Partial update | No |
| DELETE | Remove resource | Yes |
GET - Retrieve Resources
# Get all users
GET /users
# Get single user
GET /users/123
# Get with filtering
GET /users?status=active&role=admin
# Get with sorting
GET /users?sort=created_at,desc
# Get with pagination
GET /users?page=2&limit=20
POST - Create Resources
# Create new user
POST /users
Content-Type: application/json
{
"name": "John Doe",
"email": "[email protected]",
"role": "developer"
}
# Response
HTTP/1.1 201 Created
Location: /users/456
PUT vs PATCH
# PUT - Replace entire resource
PUT /users/123
Content-Type: application/json
{
"name": "John Doe",
"email": "[email protected]",
"role": "admin",
"status": "active"
}
# PATCH - Partial update
PATCH /users/123
Content-Type: application/json
{
"role": "admin"
}
DELETE - Remove Resources
# Delete single resource
DELETE /users/123
# Delete multiple
DELETE /users?status=inactive
HTTP Status Codes
Use appropriate status codes to indicate the result of the request:
Success Codes (2xx)
# 200 OK - Request successful
GET /users/123
# 201 Created - Resource successfully created
POST /users
Location: /users/456
# 204 No Content - Successful but no content to return
DELETE /users/123
Client Error Codes (4xx)
# 400 Bad Request - Invalid request syntax
POST /users
{
"email": "not-an-email" # Invalid format
}
# 401 Unauthorized - Authentication required
GET /protected-resource
# Missing Authorization header
# 403 Forbidden - Authenticated but not authorized
GET /admin/users
# User doesn't have admin role
# 404 Not Found - Resource doesn't exist
GET /users/999999
# 409 Conflict - Resource conflict
POST /users
{
"email": "[email protected]" # Email already exists
}
# 422 Unprocessable Entity - Valid syntax but semantic errors
POST /users
{
"name": "" # Required field empty
}
# 429 Too Many Requests - Rate limited
Server Error Codes (5xx)
# 500 Internal Server Error
# 502 Bad Gateway
# 503 Service Unavailable
# 504 Gateway Timeout
Complete Status Code Reference
# 1xx Informational
100 - Continue
101 - Switching Protocols
# 2xx Success
200 - OK
201 - Created
202 - Accepted
204 - No Content
# 3xx Redirection
301 - Moved Permanently
302 - Found
304 - Not Modified
# 4xx Client Errors
400 - Bad Request
401 - Unauthorized
403 - Forbidden
404 - Not Found
405 - Method Not Allowed
409 - Conflict
422 - Unprocessable Entity
429 - Too Many Requests
# 5xx Server Errors
500 - Internal Server Error
502 - Bad Gateway
503 - Service Unavailable
504 - Gateway Timeout
Error Handling
A well-designed error response helps developers debug issues quickly.
Standard Error Response Format
{
"error": {
"code": "VALIDATION_ERROR",
"message": "Invalid request parameters",
"details": [
{
"field": "email",
"message": "Invalid email format",
"value": "not-an-email"
},
{
"field": "age",
"message": "Must be greater than 0",
"value": "-5"
}
],
"requestId": "req_abc123xyz"
}
}
Error Response Examples
// Resource not found
{
"error": {
"code": "RESOURCE_NOT_FOUND",
"message": "User with ID 123 not found",
"resource": "User",
"resourceId": "123"
}
}
// Authentication failure
{
"error": {
"code": "AUTHENTICATION_FAILED",
"message": "Invalid API key",
"help": "Provide a valid Authorization header"
}
}
// Rate limiting
{
"error": {
"code": "RATE_LIMIT_EXCEEDED",
"message": "Too many requests",
"retryAfter": 60,
"limit": 100,
"remaining": 0
}
}
Best Practices for Error Handling
# Good: Consistent error structure across all endpoints
{
"error": {
"code": "ERROR_CODE",
"message": "Human-readable message",
"details": [] # Optional array for validation errors
}
}
# Bad: Inconsistent error formats
{"message": "Error"} # Wrong
{"error": "Something went wrong"} # Wrong
{"status": "error", "msg": "Failed"} # Wrong
Pagination
Always paginate endpoints that return collections:
Offset-Based Pagination
GET /users?page=1&limit=20
{
"data": [
{"id": 1, "name": "User 1"},
{"id": 2, "name": "User 2"}
],
"pagination": {
"page": 1,
"limit": 20,
"total": 150,
"totalPages": 8
}
}
Cursor-Based Pagination
GET /users?cursor=eyJpZCI6MTAwfQ&limit=20
{
"data": [
{"id": 101, "name": "User 101"},
{"id": 102, "name": "User 102"}
],
"pagination": {
"nextCursor": "eyJpZCI6MTIwfQ",
"previousCursor": "eyJpZCI6OTB9",
"hasMore": true
}
}
When to Use Each
- Offset: When users need to jump to specific pages
- Cursor: When dealing with real-time data or large datasets
Filtering, Sorting, and Field Selection
Filtering
# Single filter
GET /users?status=active
# Multiple filters
GET /users?status=active&role=admin
# Range filters
GET /products?price_min=10&price_max=100
GET /orders?created_after=2024-01-01
# In filters
GET /users?role_in=admin,moderator
# Exclude filters
GET /users?status_ne=deleted
Sorting
# Single sort
GET /users?sort=created_at
# Descending sort
GET /users?sort=created_at,desc
# Multiple sort fields
GET /users?sort=status,asc;created_at,desc
Field Selection
# Return only specific fields
GET /users?fields=id,name,email
# Exclude specific fields
GET /users?exclude=password,token
API Versioning
Version your API to allow for breaking changes:
URL Path Versioning (Recommended)
# Version in path
GET /v1/users
GET /v2/users
# Response header
Accept: application/vnd.myapp.v2+json
Query Parameter Versioning
GET /users?version=2
Header Versioning
GET /users
Header: X-API-Version: 2
Best Practices Summary
Do’s
- Use nouns for resource names
- Use proper HTTP methods
- Return appropriate status codes
- Implement pagination for collections
- Version your API
- Use consistent error formats
- Document your API
- Implement rate limiting
Don’ts
- Use verbs in URLs
- Return 200 for errors
- Expose sensitive data in errors
- Use generic error messages
- Skip versioning
- Return different structures for same status code
External Resources
- RESTful Web APIs (O’Reilly)
- Google API Design Guide
- Microsoft REST API Guidelines
- RFC 7231 - HTTP/1.1 Semantics
- Richardson Maturity Model
Comments