RESTful API Design: Best Practices for Building Scalable APIs
Learn how to design robust, scalable RESTful APIs with proper resource naming, versioning, authentication, error handling, and documentation strategies.

A well-designed API is the foundation of any successful application. This guide covers best practices for designing RESTful APIs that are intuitive, scalable, and maintainable.
Resource Naming Conventions#
Use Nouns, Not Verbs#
# Good - Resources are nouns
GET /users # List users
POST /users # Create user
GET /users/123 # Get user
PUT /users/123 # Update user
DELETE /users/123 # Delete user
# Bad - Verbs in URLs
GET /getUsers
POST /createUser
GET /getUserById/123
Hierarchical Resources#
# Nested resources for relationships
GET /users/123/orders # User's orders
GET /users/123/orders/456 # Specific order
POST /users/123/orders # Create order for user
# Alternative: Query parameters for filtering
GET /orders?user_id=123 # Filter orders by user
Use plural nouns for collections (/users) and singular identifiers for specific resources (/users/123).
Consistent Naming#
# Use kebab-case for multi-word resources
GET /user-profiles
GET /order-items
GET /shipping-addresses
# Use snake_case for query parameters
GET /products?category_id=5&sort_by=price&sort_order=desc
HTTP Methods and Status Codes#
Proper Method Usage#
GET - Retrieve resources (idempotent, cacheable)
POST - Create new resources
PUT - Replace entire resource (idempotent)
PATCH - Partial update (not idempotent)
DELETE - Remove resource (idempotent)
OPTIONS - Get allowed methods
HEAD - Get headers only
Status Code Reference#
// Success responses
200 OK // Successful GET, PUT, PATCH
201 Created // Successful POST (include Location header)
204 No Content // Successful DELETE
// Client errors
400 Bad Request // Invalid request body/parameters
401 Unauthorized // Missing or invalid authentication
403 Forbidden // Authenticated but not authorized
404 Not Found // Resource doesn't exist
409 Conflict // Resource conflict (duplicate, etc.)
422 Unprocessable // Validation errors
429 Too Many Requests // Rate limit exceeded
// Server errors
500 Internal Error // Unexpected server error
502 Bad Gateway // Upstream service error
503 Service Unavailable // Temporary overloadRequest and Response Design#
Consistent Response Structure#
// Success response
{
"data": {
"id": 123,
"name": "John Doe",
"email": "john@example.com",
"created_at": "2024-01-15T10:30:00Z"
},
"meta": {
"request_id": "req_abc123"
}
}
// Collection response with pagination
{
"data": [
{ "id": 1, "name": "Product A" },
{ "id": 2, "name": "Product B" }
],
"meta": {
"total": 150,
"page": 1,
"per_page": 20,
"total_pages": 8
},
"links": {
"self": "/products?page=1",
"next": "/products?page=2",
"last": "/products?page=8"
}
}
// Error response
{
"error": {
"code": "VALIDATION_ERROR",
"message": "The request contains invalid data",
"details": [
{
"field": "email",
"message": "Invalid email format"
},
{
"field": "age",
"message": "Must be at least 18"
}
]
},
"meta": {
"request_id": "req_xyz789"
}
}Filtering, Sorting, and Pagination#
# Filtering
GET /products?category=electronics&brand=apple&min_price=100&max_price=500
# Sorting
GET /products?sort=price:asc,created_at:desc
# Pagination (offset-based)
GET /products?page=2&per_page=20
# Pagination (cursor-based - better for large datasets)
GET /products?cursor=eyJpZCI6MTAwfQ&limit=20
# Field selection (sparse fieldsets)
GET /users/123?fields=id,name,email
# Including related resources
GET /orders/123?include=user,items,shipping_address
API Versioning#
URL Path Versioning (Recommended)#
GET /v1/users
GET /v2/users
# Clear and explicit
# Easy to route and cache
# Visible in logs and documentation
Header Versioning#
GET /users
Accept: application/vnd.myapi.v2+json
# Cleaner URLs
# More complex to implement
# Harder to test in browser
Version Deprecation Strategy#
// Response headers for deprecated versions
{
"headers": {
"Deprecation": "Sun, 01 Jan 2025 00:00:00 GMT",
"Sunset": "Sun, 01 Jul 2025 00:00:00 GMT",
"Link": "</v2/users>; rel=\"successor-version\""
}
}Authentication and Authorization#
JWT Authentication#
// Request with Bearer token
GET /api/users
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
// Token structure
{
"header": {
"alg": "RS256",
"typ": "JWT"
},
"payload": {
"sub": "user_123",
"email": "user@example.com",
"roles": ["user", "admin"],
"iat": 1704067200,
"exp": 1704153600
}
}API Key Authentication#
// Header-based (recommended)
GET /api/data
X-API-Key: sk_live_abc123xyz
// Query parameter (less secure - visible in logs)
GET /api/data?api_key=sk_live_abc123xyzOAuth 2.0 Scopes#
# Define granular permissions
read:users - Read user data
write:users - Create/update users
delete:users - Delete users
read:orders - Read order data
admin - Full access
# Token with scopes
{
"access_token": "...",
"token_type": "Bearer",
"expires_in": 3600,
"scope": "read:users read:orders"
}
Rate Limiting#
Implementation Headers#
HTTP/1.1 200 OK
X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 999
X-RateLimit-Reset: 1704067200
# When rate limited
HTTP/1.1 429 Too Many Requests
Retry-After: 60
X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1704067200
Rate Limit Strategies#
// Sliding window implementation
const rateLimiter = {
// Per-user limits
authenticated: {
requests: 1000,
window: '1 hour'
},
// Per-IP limits for unauthenticated
anonymous: {
requests: 100,
window: '1 hour'
},
// Per-endpoint limits
endpoints: {
'POST /auth/login': { requests: 5, window: '1 minute' },
'POST /users': { requests: 10, window: '1 hour' }
}
};Error Handling#
Comprehensive Error Response#
{
"error": {
"code": "RESOURCE_NOT_FOUND",
"message": "The requested user was not found",
"details": {
"resource_type": "User",
"resource_id": "123"
},
"documentation_url": "https://api.example.com/docs/errors#RESOURCE_NOT_FOUND",
"request_id": "req_abc123"
}
}Error Code Catalog#
const errorCodes = {
// Authentication errors (1xxx)
INVALID_CREDENTIALS: { status: 401, message: 'Invalid email or password' },
TOKEN_EXPIRED: { status: 401, message: 'Authentication token has expired' },
INSUFFICIENT_PERMISSIONS: { status: 403, message: 'You do not have permission' },
// Validation errors (2xxx)
VALIDATION_ERROR: { status: 422, message: 'Request validation failed' },
INVALID_FORMAT: { status: 400, message: 'Invalid request format' },
// Resource errors (3xxx)
RESOURCE_NOT_FOUND: { status: 404, message: 'Resource not found' },
RESOURCE_CONFLICT: { status: 409, message: 'Resource already exists' },
// Rate limiting (4xxx)
RATE_LIMIT_EXCEEDED: { status: 429, message: 'Too many requests' },
// Server errors (5xxx)
INTERNAL_ERROR: { status: 500, message: 'An unexpected error occurred' },
SERVICE_UNAVAILABLE: { status: 503, message: 'Service temporarily unavailable' },
};HATEOAS and Hypermedia#
Self-Describing Responses#
{
"data": {
"id": 123,
"status": "pending",
"total": 99.99
},
"links": {
"self": { "href": "/orders/123" },
"customer": { "href": "/users/456" },
"items": { "href": "/orders/123/items" }
},
"actions": {
"cancel": {
"href": "/orders/123/cancel",
"method": "POST",
"title": "Cancel this order"
},
"pay": {
"href": "/orders/123/pay",
"method": "POST",
"title": "Process payment"
}
}
}API Documentation#
OpenAPI Specification#
openapi: 3.0.3
info:
title: My API
version: 1.0.0
description: A sample API
paths:
/users:
get:
summary: List all users
parameters:
- name: page
in: query
schema:
type: integer
default: 1
responses:
'200':
description: Successful response
content:
application/json:
schema:
type: object
properties:
data:
type: array
items:
$ref: '#/components/schemas/User'
components:
schemas:
User:
type: object
properties:
id:
type: integer
name:
type: string
email:
type: string
format: emailConclusion#
Well-designed APIs are intuitive, consistent, and scalable. By following these best practices—proper resource naming, appropriate status codes, comprehensive error handling, and clear documentation—you create APIs that developers love to use.
Key takeaways:
- Use nouns for resources, HTTP methods for actions
- Return appropriate status codes
- Implement proper versioning from the start
- Design comprehensive error responses
- Document everything with OpenAPI
Related Articles
Advanced Python Tricks: Language Features Every Senior Developer Should Know
Master advanced Python language features including decorators, metaclasses, descriptors, context managers, generators, and memory optimization techniques.
System Design & Software Architecture: Building Scalable Systems
Master system design principles including distributed systems, microservices architecture, database scaling, caching strategies, and high-availability patterns for large-scale applications.
Next.js SEO Optimization: Complete Guide to Ranking Higher
Master SEO in Next.js with metadata optimization, structured data, Core Web Vitals, sitemaps, and advanced techniques for better search engine rankings.