Introduction
Well-designed APIs are crucial for developer experience and system maintainability. This guide covers API design best practices.
REST API Design
URL Structure
RESTful URL Patterns:
โโโ Resources (nouns, plural)
โ โโโ /users
โ โโโ /orders
โ โโโ /products
โ
โโโ Nested Resources
โ โโโ /users/{id}/orders
โ โโโ /orders/{id}/items
โ
โโโ Actions
โ โโโ POST /users/{id}/activate
โ โโโ POST /orders/{id}/cancel
โ
โโโ Filtering
โโโ /users?status=active
โโโ /products?category=electronics&price<100
โโโ /orders?date_from=2026-01-01
HTTP Methods
HTTP Method Usage:
โโโ GET - Retrieve resources
โ โโโ GET /users - List users
โ โโโ GET /users/123 - Get user
โ
โโโ POST - Create new resources
โ โโโ POST /users - Create user
โ
โโโ PUT - Replace entire resource
โ โโโ PUT /users/123 - Replace user
โ
โโโ PATCH - Partial update
โ โโโ PATCH /users/123 - Update user fields
โ
โโโ DELETE - Remove resources
โโโ DELETE /users/123 - Delete user
Response Format
// Success Response
{
"data": {
"id": "123",
"type": "user",
"attributes": {
"name": "John Doe",
"email": "[email protected]"
}
},
"meta": {
"request_id": "abc-123"
}
}
// Error Response
{
"error": {
"code": "VALIDATION_ERROR",
"message": "Invalid email format",
"details": [
{
"field": "email",
"message": "Must be a valid email"
}
]
}
}
Authentication
API Keys
# API Key authentication
class APIKeyAuth:
def __init__(self, header_name='X-API-Key'):
self.header_name = header_name
def authenticate(self, request):
api_key = request.headers.get(self.header_name)
if not api_key:
return None
user = self.validate_key(api_key)
return user
def validate_key(self, key):
# Look up key in database
return api_key_db.get(key)
JWT Tokens
import jwt
from datetime import datetime, timedelta
def create_access_token(user_id):
"""Create JWT access token"""
payload = {
'sub': user_id,
'exp': datetime.utcnow() + timedelta(hours=1),
'iat': datetime.utcnow()
}
return jwt.encode(payload, SECRET_KEY, algorithm='HS256')
def verify_token(token):
"""Verify JWT token"""
try:
payload = jwt.decode(token, SECRET_KEY, algorithms=['HS256'])
return payload['sub']
except jwt.ExpiredSignatureError:
return None
Versioning
URL Versioning
API Versioning Strategies:
โโโ URL Path (Recommended)
โ โโโ /v1/users
โ โโโ /v2/users
โ
โโโ Header
โ โโโ Accept: application/vnd.api+json;version=2
โ โโโ X-API-Version: 2
โ
โโโ Query Parameter
โโโ /users?version=2
โโโ Not recommended
Rate Limiting
# Rate limiting with Redis
class RateLimiter:
def __init__(self, redis_client):
self.redis = redis_client
def is_allowed(self, identifier, limit, window):
"""Check if request is allowed"""
key = f"rate_limit:{identifier}"
current = self.redis.get(key)
if current is None:
self.redis.setex(key, window, 1)
return True
if int(current) >= limit:
return False
self.redis.incr(key)
return True
Documentation
OpenAPI/Swagger
OpenAPI Specification:
openapi: 3.0.0
info:
title: User API
version: 1.0.0
paths:
/users:
get:
summary: List users
parameters:
- name: limit
in: query
schema:
type: integer
default: 20
responses:
'200':
description: Success
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/User'
Conclusion
Good API design improves developer experience and system maintainability. Follow these practices consistently.
Comments