Good API design is crucial for developer experience and system success. Here are best practices for designing RESTful APIs.
1. Use RESTful Conventions
Resource-Based URLs
# Good: Resource-based
GET /api/users
GET /api/users/123
POST /api/users
PUT /api/users/123
DELETE /api/users/123
# Bad: Action-based
GET /api/getUsers
POST /api/createUser
POST /api/deleteUser
HTTP Methods
- GET: Retrieve resources
- POST: Create resources
- PUT: Update entire resource
- PATCH: Partial update
- DELETE: Remove resource
2. Consistent Naming
Use Plural Nouns
# Good
GET /api/users
GET /api/orders
GET /api/products
# Bad
GET /api/user
GET /api/order
GET /api/product
Use kebab-case or camelCase
# Good: Consistent
GET /api/user-profiles
GET /api/orderItems
# Bad: Mixed
GET /api/user_profiles
GET /api/order-items
3. Version Your API
URL Versioning
GET /api/v1/users
GET /api/v2/users
Header Versioning
GET /api/users
Accept: application/vnd.api+json;version=2
4. Use Proper HTTP Status Codes
Success Codes
200 OK # Successful GET, PUT, PATCH
201 Created # Successful POST
204 No Content # Successful DELETE
Client Error Codes
400 Bad Request # Invalid request
401 Unauthorized # Authentication required
403 Forbidden # Not authorized
404 Not Found # Resource doesn't exist
409 Conflict # Resource conflict
422 Unprocessable # Validation errors
Server Error Codes
500 Internal Server Error
502 Bad Gateway
503 Service Unavailable
5. Consistent Response Format
Standard Response Structure
{
"data": {
"id": "123",
"type": "user",
"attributes": {
"name": "John Doe",
"email": "[email protected]"
}
},
"meta": {
"timestamp": "2025-12-10T10:00:00Z"
}
}
Error Response Format
{
"error": {
"code": "VALIDATION_ERROR",
"message": "Invalid input",
"details": [
{
"field": "email",
"message": "Invalid email format"
}
]
}
}
6. Pagination
Cursor-Based Pagination
GET /api/users?cursor=eyJpZCI6IjEyMyJ9&limit=20
Response:
{
"data": [...],
"pagination": {
"cursor": "eyJpZCI6IjE0MyJ9",
"has_more": true
}
}
Offset-Based Pagination
GET /api/users?page=1&limit=20
Response:
{
"data": [...],
"pagination": {
"page": 1,
"limit": 20,
"total": 100,
"total_pages": 5
}
}
7. Filtering and Sorting
Filtering
GET /api/users?status=active&role=admin
GET /api/orders?created_after=2024-01-01&created_before=2024-12-31
Sorting
GET /api/users?sort=name,email&order=asc,desc
GET /api/users?sort=-created_at # Descending
8. Field Selection
Sparse Fieldsets
GET /api/users?fields=id,name,email
GET /api/users/123?fields=id,name
Include Related Resources
GET /api/users/123?include=orders,profile
9. Rate Limiting
Headers
X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 999
X-RateLimit-Reset: 1609459200
Response
HTTP/1.1 429 Too Many Requests
Retry-After: 60
10. Authentication and Authorization
Use Standard Methods
# Bearer token
Authorization: Bearer <token>
# API key
X-API-Key: <key>
Return Clear Errors
{
"error": {
"code": "UNAUTHORIZED",
"message": "Invalid or expired token"
}
}
11. Documentation
OpenAPI/Swagger
openapi: 3.0.0
info:
title: User API
version: 1.0.0
paths:
/users:
get:
summary: List users
responses:
'200':
description: Success
Interactive Documentation
- Swagger UI: Visual API documentation
- Postman: API testing and docs
- Redoc: Beautiful API docs
12. Error Handling
Consistent Error Format
{
"error": {
"code": "RESOURCE_NOT_FOUND",
"message": "User with ID 123 not found",
"request_id": "req_abc123"
}
}
Validation Errors
{
"error": {
"code": "VALIDATION_ERROR",
"message": "Validation failed",
"errors": [
{
"field": "email",
"message": "Invalid email format",
"code": "INVALID_FORMAT"
}
]
}
}
13. Caching
Cache Headers
Cache-Control: public, max-age=3600
ETag: "33a64df551425fcc55e4d42a148795d9f25f89d4"
Last-Modified: Wed, 21 Oct 2015 07:28:00 GMT
Conditional Requests
GET /api/users/123
If-None-Match: "33a64df551425fcc55e4d42a148795d9f25f89d4"
# 304 Not Modified if unchanged
14. Security Best Practices
Use HTTPS
Always use HTTPS in production.
Input Validation
// Validate all inputs
const schema = z.object({
email: z.string().email(),
age: z.number().min(0).max(120),
});
const result = schema.safeParse(req.body);
Sanitize Output
// Sanitize user input
const sanitized = sanitizeHtml(userInput, {
allowedTags: [],
allowedAttributes: {},
});
15. Performance Optimization
Compression
Content-Encoding: gzip
Database Optimization
- Use indexes
- Avoid N+1 queries
- Use pagination
- Cache frequently accessed data
Response Time
- Target: < 200ms for simple queries
- Acceptable: < 1s for complex queries
- Timeout: Set reasonable timeouts
Tools
API Development
- Postman: API testing
- Insomnia: API client
- HTTPie: Command-line client
Documentation
- Swagger/OpenAPI: API specification
- Redoc: Documentation generator
- Stoplight: API design platform
Testing
- Jest: Unit testing
- Supertest: API testing
- Newman: Postman CLI
Conclusion
Good API design:
- Follows conventions: RESTful principles
- Is consistent: Predictable patterns
- Is documented: Clear documentation
- Is secure: Authentication and validation
- Is performant: Fast and efficient
Remember: Your API is a product. Design it with your users (developers) in mind.
Key principles:
- Consistency: Same patterns throughout
- Simplicity: Easy to understand and use
- Documentation: Clear and comprehensive
- Versioning: Plan for changes
- Performance: Fast and efficient
Happy API designing! ๐