RESTful API Design
Resource-Oriented
Design APIs around resources and their relationships
Stateless
Each request contains all information needed
Uniform Interface
Consistent resource identification and manipulation
URL Structure Best Practices
Good Practices
# Collection endpoints
GET /api/v1/users
POST /api/v1/users
# Single resource endpoints
GET /api/v1/users/{id}
PUT /api/v1/users/{id}
PATCH /api/v1/users/{id}
DELETE /api/v1/users/{id}
# Nested resources
GET /api/v1/users/{id}/orders
POST /api/v1/users/{id}/orders
# Filtering and pagination
GET /api/v1/users?role=admin&status=active
GET /api/v1/users?page=2&limit=20
Anti-Patterns
# Avoid verbs in URLs
❌ /api/v1/getUsers
❌ /api/v1/createUser
# Avoid deep nesting
❌ /api/v1/users/{id}/orders/{orderId}/items/{itemId}
# Avoid query operations in URL
❌ /api/v1/users/search/findByEmail
❌ /api/v1/users/filter
Response Structure
Success Response
{
"status": "success",
"data": {
"id": "123",
"name": "John Doe",
"email": "john@example.com",
"created_at": "2024-02-01T10:00:00Z"
},
"meta": {
"version": "1.0",
"timestamp": "2024-02-01T10:00:00Z"
}
}
Error Response
{
"status": "error",
"error": {
"code": "VALIDATION_ERROR",
"message": "Invalid input data",
"details": [
{
"field": "email",
"message": "Invalid email format"
}
]
}
}
API Versioning
URL Versioning
/api/v1/users
/api/v2/users
Pros
- Simple to implement
- Explicit versioning
- Easy to document
Cons
- URL pollution
- Not RESTful purist
Header Versioning
Accept: application/vnd.company.api+json;version=1
Accept: application/vnd.company.api+json;version=2
Pros
- Clean URLs
- More RESTful
Cons
- More complex
- Less visible
Authentication Patterns
JWT Authentication
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Implementation
// Token generation
const token = jwt.sign(
{ userId: user.id },
process.env.JWT_SECRET,
{ expiresIn: '24h' }
);
// Token verification
const verifyToken = (req, res, next) => {
const token = req.headers.authorization?.split(' ')[1];
if (!token) {
return res.status(401).json({
status: 'error',
error: {
code: 'UNAUTHORIZED',
message: 'No token provided'
}
});
}
try {
const decoded = jwt.verify(token, process.env.JWT_SECRET);
req.user = decoded;
next();
} catch (error) {
return res.status(401).json({
status: 'error',
error: {
code: 'INVALID_TOKEN',
message: 'Invalid token'
}
});
}
};
API Documentation
openapi: 3.0.0
info:
title: User Management API
version: 1.0.0
paths:
/users:
get:
summary: List all users
parameters:
- in: query
name: role
schema:
type: string
- in: query
name: status
schema:
type: string
responses:
'200':
description: List of users
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/User'
post:
summary: Create a new user
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/UserInput'
responses:
'201':
description: User created