Well-designed REST APIs are the foundation of scalable applications. Good API design improves developer experience, reduces integration time, and makes maintenance easier. This guide covers REST principles, versioning strategies, and best practices for production APIs.
REST Principles
- Resource-Based: URLs represent resources, not actions
- Stateless: Each request contains all necessary information
- HTTP Methods: Use appropriate methods (GET, POST, PUT, DELETE, PATCH)
- Status Codes: Return appropriate HTTP status codes
- JSON: Use JSON for request/response bodies
Resource Naming Conventions
Good Resource Names
✅ GET /api/users
✅ GET /api/users/123
✅ GET /api/users/123/posts
✅ POST /api/users
✅ PUT /api/users/123
✅ DELETE /api/users/123
✅ GET /api/users/123/comments?limit=10
Bad Resource Names
❌ GET /api/getUser
❌ POST /api/createUser
❌ GET /api/user?id=123
❌ POST /api/deleteUser
❌ GET /api/users/getUserPosts
HTTP Methods Usage
GET - Retrieve Resources
GET /api/users
GET /api/users/123
GET /api/users?status=active&page=1&limit=20
// Response
{
"data": [...],
"pagination": {
"page": 1,
"limit": 20,
"total": 100,
"totalPages": 5
}
}
POST - Create Resources
POST /api/users
Content-Type: application/json
{
"name": "John Doe",
"email": "john@example.com"
}
// Response: 201 Created
{
"id": 123,
"name": "John Doe",
"email": "john@example.com",
"createdAt": "2024-12-01T10:00:00Z"
}
PUT - Full Update
PUT /api/users/123
Content-Type: application/json
{
"name": "John Smith",
"email": "johnsmith@example.com"
}
// Replaces entire resource
PATCH - Partial Update
PATCH /api/users/123
Content-Type: application/json
{
"email": "newemail@example.com"
}
// Updates only specified fields
DELETE - Remove Resources
DELETE /api/users/123
// Response: 204 No Content (or 200 with confirmation)
HTTP Status Codes
- 200 OK: Successful GET, PUT, PATCH
- 201 Created: Successful POST
- 204 No Content: Successful DELETE
- 400 Bad Request: Invalid request syntax
- 401 Unauthorized: Authentication required
- 403 Forbidden: Insufficient permissions
- 404 Not Found: Resource doesn't exist
- 409 Conflict: Resource conflict (e.g., duplicate email)
- 422 Unprocessable Entity: Validation errors
- 429 Too Many Requests: Rate limit exceeded
- 500 Internal Server Error: Server error
API Versioning Strategies
URL Versioning
GET /api/v1/users
GET /api/v2/users
// Pros: Clear, easy to cache
// Cons: Changes URL structure
Header Versioning
GET /api/users
Accept: application/vnd.api+json;version=2
// Pros: Clean URLs
// Cons: Harder to cache, less discoverable
Query Parameter Versioning
GET /api/users?version=2
// Pros: Simple
// Cons: Doesn't affect resources, can be confusing
Error Handling
Consistent Error Format
// Error Response Structure
{
"error": {
"code": "VALIDATION_ERROR",
"message": "Validation failed",
"details": [
{
"field": "email",
"message": "Invalid email format"
}
],
"requestId": "req_123abc",
"timestamp": "2024-12-01T10:00:00Z"
}
}
// Implementation
function errorHandler(err, req, res, next) {
const statusCode = err.statusCode || 500;
const error = {
code: err.code || 'INTERNAL_ERROR',
message: err.message || 'An error occurred',
...(err.details && { details: err.details }),
requestId: req.id,
timestamp: new Date().toISOString(),
};
res.status(statusCode).json({ error });
}
Pagination
// Cursor-based pagination (recommended for large datasets)
GET /api/users?cursor=eyJpZCI6MTIzfQ&limit=20
Response:
{
"data": [...],
"pagination": {
"cursor": "eyJpZCI6MTQzfQ",
"hasMore": true,
"limit": 20
}
}
// Offset-based pagination
GET /api/users?page=2&limit=20
Response:
{
"data": [...],
"pagination": {
"page": 2,
"limit": 20,
"total": 100,
"totalPages": 5
}
}
Filtering, Sorting, and Searching
// Filtering
GET /api/users?status=active&role=admin
// Sorting
GET /api/users?sort=createdAt&order=desc
// Searching
GET /api/users?search=john&fields=name,email
// Combined
GET /api/users?status=active&sort=createdAt&order=desc&page=1&limit=20
API Security
- Use HTTPS for all requests
- Implement authentication (JWT, OAuth2)
- Validate and sanitize all inputs
- Rate limiting to prevent abuse
- CORS configuration for web clients
- API keys for service-to-service communication
API Documentation
Document your APIs comprehensively using OpenAPI/Swagger:
- Clear endpoint descriptions
- Request/response examples
- Error codes and meanings
- Authentication requirements
- Rate limits and quotas
Real-World Example
Well-designed e-commerce API:
GET /api/v1/products // List products
GET /api/v1/products/123 // Get product
POST /api/v1/products // Create product
PATCH /api/v1/products/123 // Update product
DELETE /api/v1/products/123 // Delete product
GET /api/v1/products/123/reviews // Product reviews
POST /api/v1/products/123/reviews // Create review
GET /api/v1/orders // User's orders
POST /api/v1/orders // Create order
GET /api/v1/orders/456 // Order details
PATCH /api/v1/orders/456/status // Update status
Best Practices
- Use nouns for resources, not verbs
- Keep URLs consistent and predictable
- Version your API from the start
- Return consistent response formats
- Implement proper error handling
- Use pagination for list endpoints
- Support filtering and sorting
- Document everything thoroughly
- Monitor API usage and performance
- Deprecate old versions gracefully
Conclusion
Good REST API design is crucial for scalable applications. Follow REST principles, use appropriate HTTP methods and status codes, implement proper versioning, and handle errors consistently. Focus on developer experience—make your API intuitive, well-documented, and easy to integrate. Remember, good API design reduces support burden and increases adoption.