8.5 KiB
8.5 KiB
API Design Patterns Reference
RESTful API Best Practices
Resource Naming
- Use plural nouns:
/api/users,/api/products - Use kebab-case for multi-word resources:
/api/order-items - Avoid verbs in URLs (use HTTP methods instead)
HTTP Methods
GET- Retrieve resources (safe, idempotent)POST- Create new resourcesPUT- Full update (idempotent)PATCH- Partial updateDELETE- Remove resources (idempotent)
Standard Endpoints Pattern
GET /api/users - List all (with pagination)
POST /api/users - Create new
GET /api/users/:id - Get single
PUT /api/users/:id - Full update
PATCH /api/users/:id - Partial update
DELETE /api/users/:id - Delete
GET /api/users/:id/posts - Nested resource
Pagination
Query Parameters:
GET /api/users?page=1&limit=20
GET /api/users?offset=0&limit=20
Response Format:
{
"data": [...],
"pagination": {
"page": 1,
"limit": 20,
"total": 150,
"totalPages": 8
}
}
Cursor-Based (for large datasets):
GET /api/users?cursor=eyJpZCI6MTIzfQ&limit=20
Response:
{
"data": [...],
"nextCursor": "eyJpZCI6MTQzfQ",
"hasMore": true
}
Filtering
GET /api/products?category=electronics&price[gte]=100&price[lte]=500
GET /api/users?status=active&role=admin
GET /api/posts?author=123&published=true
Sorting
GET /api/products?sort=price # Ascending
GET /api/products?sort=-price # Descending (minus prefix)
GET /api/products?sort=category,price # Multiple fields
Searching
GET /api/products?q=laptop
GET /api/users?search=john&fields=name,email
GET /api/posts?fulltext=artificial+intelligence
Response Formats
Success Response:
{
"success": true,
"data": { ... },
"message": "User created successfully"
}
Error Response:
{
"success": false,
"error": {
"code": "VALIDATION_ERROR",
"message": "Invalid input data",
"details": [
{
"field": "email",
"message": "Email is required"
}
]
}
}
List Response:
{
"success": true,
"data": [...],
"pagination": { ... },
"meta": {
"count": 20,
"total": 150
}
}
Status Codes
200 OK- Successful GET, PUT, PATCH, DELETE201 Created- Successful POST204 No Content- Successful DELETE (no response body)400 Bad Request- Invalid input401 Unauthorized- Missing/invalid authentication403 Forbidden- Authenticated but not authorized404 Not Found- Resource doesn't exist409 Conflict- Resource already exists422 Unprocessable Entity- Validation errors429 Too Many Requests- Rate limit exceeded500 Internal Server Error- Server error
Authentication Patterns
JWT Authentication
Login Endpoint:
POST /api/auth/login
{
"email": "user@example.com",
"password": "password123"
}
Response:
{
"success": true,
"data": {
"user": { ... },
"accessToken": "eyJhbGc...",
"refreshToken": "eyJhbGc...",
"expiresIn": 3600
}
}
Protected Route Header:
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...
Refresh Token:
POST /api/auth/refresh
{
"refreshToken": "eyJhbGc..."
}
Response:
{
"accessToken": "eyJhbGc...",
"expiresIn": 3600
}
API Key Authentication
X-API-Key: your-api-key-here
Session-Based Authentication
POST /api/auth/login
Response sets cookie:
Set-Cookie: sessionId=abc123; HttpOnly; Secure; SameSite=Strict
Advanced Patterns
Nested Resources
GET /api/users/123/posts - User's posts
POST /api/users/123/posts - Create post for user
GET /api/posts/456/comments - Post's comments
Bulk Operations
POST /api/users/bulk
{
"operation": "create",
"data": [
{ "name": "User 1", "email": "user1@example.com" },
{ "name": "User 2", "email": "user2@example.com" }
]
}
Response:
{
"success": true,
"results": [
{ "id": 1, "status": "created" },
{ "id": 2, "status": "created" }
],
"summary": {
"total": 2,
"succeeded": 2,
"failed": 0
}
}
Batch Requests
POST /api/batch
{
"requests": [
{ "method": "GET", "url": "/api/users/123" },
{ "method": "GET", "url": "/api/posts/456" }
]
}
Response:
{
"responses": [
{ "status": 200, "body": { ... } },
{ "status": 200, "body": { ... } }
]
}
Webhooks
POST /api/webhooks
{
"url": "https://example.com/webhook",
"events": ["user.created", "order.completed"],
"secret": "webhook-secret"
}
File Upload
Single File:
POST /api/files
Content-Type: multipart/form-data
Response:
{
"success": true,
"data": {
"id": "file-123",
"url": "https://cdn.example.com/files/file-123.jpg",
"size": 1024000,
"type": "image/jpeg"
}
}
Presigned URL (for direct upload):
POST /api/files/presign
{
"filename": "photo.jpg",
"contentType": "image/jpeg"
}
Response:
{
"uploadUrl": "https://s3.amazonaws.com/...",
"fileId": "file-123",
"expiresIn": 3600
}
Rate Limiting Headers
X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 999
X-RateLimit-Reset: 1640995200
Versioning
URL Versioning (recommended):
/api/v1/users
/api/v2/users
Header Versioning:
Accept: application/vnd.api.v2+json
Query Parameter:
/api/users?version=2
GraphQL Patterns
Query Structure
query GetUser($id: ID!) {
user(id: $id) {
id
name
email
posts {
id
title
createdAt
}
}
}
Mutation Structure
mutation CreateUser($input: CreateUserInput!) {
createUser(input: $input) {
user {
id
name
email
}
errors {
field
message
}
}
}
Pagination (Relay-style)
query GetUsers($first: Int, $after: String) {
users(first: $first, after: $after) {
edges {
node {
id
name
}
cursor
}
pageInfo {
hasNextPage
endCursor
}
}
}
Error Handling
Structured Error Codes
const ErrorCodes = {
VALIDATION_ERROR: 'VALIDATION_ERROR',
AUTHENTICATION_ERROR: 'AUTHENTICATION_ERROR',
AUTHORIZATION_ERROR: 'AUTHORIZATION_ERROR',
NOT_FOUND: 'NOT_FOUND',
DUPLICATE_RESOURCE: 'DUPLICATE_RESOURCE',
RATE_LIMIT_EXCEEDED: 'RATE_LIMIT_EXCEEDED',
INTERNAL_ERROR: 'INTERNAL_ERROR'
};
Error Response Structure
{
"success": false,
"error": {
"code": "VALIDATION_ERROR",
"message": "Validation failed",
"details": [
{
"field": "email",
"code": "INVALID_FORMAT",
"message": "Email format is invalid"
}
],
"requestId": "req-123abc"
}
}
Request/Response Examples
Create User
POST /api/users
Content-Type: application/json
{
"name": "John Doe",
"email": "john@example.com",
"password": "SecurePass123!",
"role": "user"
}
Response (201 Created):
{
"success": true,
"data": {
"id": "user-123",
"name": "John Doe",
"email": "john@example.com",
"role": "user",
"createdAt": "2024-12-09T10:30:00Z"
}
}
Update User
PATCH /api/users/user-123
Content-Type: application/json
{
"name": "John Smith"
}
Response (200 OK):
{
"success": true,
"data": {
"id": "user-123",
"name": "John Smith",
"email": "john@example.com",
"updatedAt": "2024-12-09T11:00:00Z"
}
}
Search with Filters
GET /api/products?q=laptop&category=electronics&price[gte]=500&sort=-price&page=1&limit=20
Response (200 OK):
{
"success": true,
"data": [
{
"id": "prod-1",
"name": "Gaming Laptop Pro",
"category": "electronics",
"price": 1299.99
}
],
"pagination": {
"page": 1,
"limit": 20,
"total": 45,
"totalPages": 3
}
}
Security Best Practices
- Always validate input - Use validation libraries (Joi, Zod, Yup)
- Sanitize data - Prevent SQL injection, XSS
- Use HTTPS - Always encrypt in production
- Implement rate limiting - Prevent abuse
- Set security headers - Use helmet.js or equivalent
- Validate authentication - On every protected route
- Use prepared statements - For database queries
- Log securely - Don't log sensitive data
- Handle errors safely - Don't expose internal details
- Keep dependencies updated - Regular security patches