Backend Architect
Guidelines for scalable API design and microservices architecture.
Core Principles
- •Clear service boundaries - Each service owns its data
- •Contract-first APIs - Design before implementation
- •Data consistency - Understand CAP tradeoffs
- •Plan for scale - Horizontal scaling from day one
- •Keep it simple - Avoid premature optimization
RESTful API Design
Resource Naming
code
# Good - nouns, plural, hierarchical
GET /users
GET /users/{id}
GET /users/{id}/orders
POST /users/{id}/orders
GET /orders/{id}/items
# Bad - verbs, singular, flat
GET /getUser
POST /createOrder
GET /user-order-list
HTTP Methods
| Method | Purpose | Idempotent | Body |
|---|---|---|---|
| GET | Read resource | Yes | No |
| POST | Create resource | No | Yes |
| PUT | Replace resource | Yes | Yes |
| PATCH | Partial update | No | Yes |
| DELETE | Remove resource | Yes | No |
Response Codes
code
200 OK - Successful GET/PUT/PATCH 201 Created - Successful POST (include Location header) 204 No Content - Successful DELETE 400 Bad Request - Invalid input 401 Unauthorized - Missing/invalid auth 403 Forbidden - Valid auth, insufficient permissions 404 Not Found - Resource doesn't exist 409 Conflict - State conflict (duplicate, version mismatch) 422 Unprocessable - Valid syntax, invalid semantics 429 Too Many Reqs - Rate limited 500 Internal Error - Server error (log details, return generic msg)
Request/Response Format
json
// POST /users
{
"email": "user@example.com",
"name": "John Doe",
"role": "member"
}
// Response 201 Created
{
"id": "usr_abc123",
"email": "user@example.com",
"name": "John Doe",
"role": "member",
"createdAt": "2024-01-15T10:30:00Z",
"updatedAt": "2024-01-15T10:30:00Z"
}
// Error Response
{
"error": {
"code": "VALIDATION_ERROR",
"message": "Invalid request parameters",
"details": [
{ "field": "email", "message": "Invalid email format" }
]
}
}
Pagination
code
GET /users?page=2&limit=20
GET /users?cursor=eyJpZCI6MTAwfQ&limit=20
Response:
{
"data": [...],
"pagination": {
"total": 150,
"page": 2,
"limit": 20,
"hasMore": true,
"nextCursor": "eyJpZCI6MTIwfQ"
}
}
Filtering and Sorting
code
GET /orders?status=pending&createdAfter=2024-01-01 GET /orders?sort=-createdAt,+total GET /users?fields=id,name,email
API Versioning
code
# URL path (recommended) GET /v1/users GET /v2/users # Header GET /users Accept: application/vnd.api+json;version=2 # Query param GET /users?version=2
Database Schema Design
Users and Auth
sql
CREATE TABLE users (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
email VARCHAR(255) UNIQUE NOT NULL,
email_verified_at TIMESTAMPTZ,
password_hash VARCHAR(255) NOT NULL,
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW()
);
CREATE TABLE sessions (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id UUID REFERENCES users(id) ON DELETE CASCADE,
token_hash VARCHAR(255) UNIQUE NOT NULL,
expires_at TIMESTAMPTZ NOT NULL,
created_at TIMESTAMPTZ DEFAULT NOW(),
INDEX idx_sessions_user (user_id),
INDEX idx_sessions_expires (expires_at)
);
Soft Deletes
sql
ALTER TABLE users ADD COLUMN deleted_at TIMESTAMPTZ; -- Query active users SELECT * FROM users WHERE deleted_at IS NULL; -- Create view for convenience CREATE VIEW active_users AS SELECT * FROM users WHERE deleted_at IS NULL;
Optimistic Locking
sql
ALTER TABLE orders ADD COLUMN version INTEGER DEFAULT 1; -- Update with version check UPDATE orders SET status = 'shipped', version = version + 1 WHERE id = $1 AND version = $2; -- If rows affected = 0, version conflict occurred
Microservice Patterns
Service Boundaries
code
┌─────────────────┐ ┌─────────────────┐
│ User Service │ │ Order Service │
│ - Registration │ │ - Cart │
│ - Auth │ │ - Checkout │
│ - Profile │ │ - History │
│ [users DB] │ │ [orders DB] │
└────────┬────────┘ └────────┬────────┘
│ │
└───────────┬───────────┘
│
┌──────┴──────┐
│ API Gateway │
│ - Routing │
│ - Auth │
│ - Rate Lim │
└─────────────┘
Inter-Service Communication
Synchronous (HTTP/gRPC)
- •Request/response pattern
- •Use for queries, real-time needs
- •Include timeouts and circuit breakers
Asynchronous (Message Queue)
- •Event-driven pattern
- •Use for commands, eventual consistency
- •Enables loose coupling
Event-Driven Architecture
typescript
// Event structure
interface DomainEvent {
id: string;
type: string;
aggregateId: string;
timestamp: string;
data: unknown;
}
// OrderCreated event
{
"id": "evt_123",
"type": "order.created",
"aggregateId": "ord_456",
"timestamp": "2024-01-15T10:30:00Z",
"data": {
"userId": "usr_789",
"total": 99.99,
"items": [...]
}
}
Caching Strategies
Cache-Aside Pattern
typescript
async function getUser(id: string): Promise<User> {
// Check cache first
const cached = await cache.get(`user:${id}`);
if (cached) return JSON.parse(cached);
// Cache miss - fetch from DB
const user = await db.users.findById(id);
// Populate cache
await cache.set(`user:${id}`, JSON.stringify(user), 'EX', 3600);
return user;
}
Cache Invalidation
typescript
// Invalidate on write
async function updateUser(id: string, data: Partial<User>) {
await db.users.update(id, data);
await cache.del(`user:${id}`);
}
// Use cache tags for related data
await cache.set(`user:${id}`, data, { tags: [`user:${id}`, 'users'] });
await cache.invalidateTag('users'); // Clear all user caches
Authentication Patterns
JWT Structure
typescript
// Access Token (short-lived, 15min)
{
"sub": "usr_123",
"email": "user@example.com",
"roles": ["user"],
"exp": 1705312200,
"iat": 1705311300
}
// Refresh Token (long-lived, stored in DB)
{
"sub": "usr_123",
"jti": "tok_abc", // Unique ID for revocation
"exp": 1707903300
}
Auth Flow
code
1. POST /auth/login → { accessToken, refreshToken }
2. GET /api/users (Authorization: Bearer <accessToken>)
3. POST /auth/refresh → { accessToken } (when expired)
4. POST /auth/logout → Revoke refresh token
Rate Limiting
typescript
// Token bucket algorithm
interface RateLimiter {
identifier: string; // IP or user ID
maxRequests: number; // e.g., 100
windowMs: number; // e.g., 60000 (1 minute)
}
// Response headers
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 42
X-RateLimit-Reset: 1705312200
Health Checks
typescript
// GET /health
{
"status": "healthy",
"version": "1.2.3",
"timestamp": "2024-01-15T10:30:00Z",
"checks": {
"database": { "status": "up", "latency": 5 },
"cache": { "status": "up", "latency": 1 },
"queue": { "status": "up" }
}
}