API Gateway Patterns and Implementation Strategies
API gateways serve as the single entry point for client applications to access microservices, providing essential cross-cutting concerns like authentication, rate limiting, routing, and monitoring. Understanding gateway patterns is crucial for building scalable and secure distributed systems.
Core API Gateway Architecture
Gateway Placement in Microservices
API Gateway as Single Entry Point:
Internet ──► API Gateway ──► Service Registry
│ │ │
▼ ▼ ▼
Clients ──────────┼───────────────┼─► Microservice A
│ │
└───────────────┼─► Microservice B
│
└─► Microservice C
Gateway Responsibilities:
- Request Routing: Direct requests to appropriate services
- Authentication: Validate user credentials and permissions
- Rate Limiting: Control request rates to prevent abuse
- Load Balancing: Distribute requests across service instances
- Monitoring: Track request metrics and health
Routing Patterns
Path-Based Routing
URL Path Mapping:
/api/v1/users/* ──► User Service
/api/v1/orders/* ──► Order Service
/api/v1/products/* ──► Product Service
Implementation Example:
location /api/v1/users/ {
proxy_pass http://user-service:8080/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
location /api/v1/orders/ {
proxy_pass http://order-service:8080/;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
Header-Based Routing
Content-Type Routing:
Content-Type: application/json ──► JSON Service
Content-Type: application/xml ──► XML Service
Version-Based Routing:
X-API-Version: v1 ──► Legacy Service
X-API-Version: v2 ──► Modern Service
Dynamic Routing with Service Discovery
Service Registry Integration:
API Gateway ──► Consul/Eureka ──► Service Instances
│ │
└─► Health Check ──► Load Balance ──► Route Request
Authentication and Authorization
JWT Token Validation
JWT Flow:
Client ──► JWT Token ──► Gateway ──► Validate Token ──► Service
▲ │ │
└──────────────────────┴───────────────┘
Token Refresh
Token Validation Implementation:
class JwtAuthMiddleware {
async validateToken(token: string): Promise<UserClaims> {
try {
const decoded = jwt.verify(token, process.env.JWT_SECRET);
return {
userId: decoded.sub,
roles: decoded.roles,
permissions: decoded.permissions
};
} catch (error) {
throw new AuthenticationError('Invalid token');
}
}
async authorizeRequest(claims: UserClaims, requiredRoles: string[]): Promise<boolean> {
return requiredRoles.some(role => claims.roles.includes(role));
}
}
OAuth2 Integration
Authorization Code Flow:
Client ──► Gateway ──► Auth Server ──► User Consent ──► Tokens
▲ │ │ │
└──────────────┼────────────────────┼────────────────────┘
Redirect with Code ──► Exchange for Tokens
API Key Authentication
Key-Based Access:
class ApiKeyMiddleware {
private keyStore: Map<string, ApiKeyConfig> = new Map();
async validateApiKey(apiKey: string): Promise<boolean> {
const config = this.keyStore.get(apiKey);
if (!config) return false;
// Check rate limits, expiration, etc.
return config.active && !this.isExpired(config);
}
async getRateLimit(apiKey: string): Promise<RateLimit> {
const config = this.keyStore.get(apiKey);
return config?.rateLimit || { requests: 100, period: '1h' };
}
}
Rate Limiting Patterns
Token Bucket Algorithm
Token Bucket Implementation:
class TokenBucket {
private tokens: number;
private lastRefill: number;
private readonly capacity: number;
private readonly refillRate: number; // tokens per second
constructor(capacity: number, refillRate: number) {
this.capacity = capacity;
this.refillRate = refillRate;
this.tokens = capacity;
this.lastRefill = Date.now();
}
allowRequest(): boolean {
this.refill();
if (this.tokens >= 1) {
this.tokens--;
return true;
}
return false;
}
private refill(): void {
const now = Date.now();
const timePassed = (now - this.lastRefill) / 1000;
const tokensToAdd = timePassed * this.refillRate;
this.tokens = Math.min(this.capacity, this.tokens + tokensToAdd);
this.lastRefill = now;
}
}
Sliding Window Counter
Distributed Rate Limiting:
class SlidingWindowLimiter {
private redis: Redis;
async isAllowed(key: string, limit: number, windowMs: number): Promise<boolean> {
const now = Date.now();
const windowStart = now - windowMs;
// Remove old entries
await this.redis.zremrangebyscore(key, 0, windowStart);
// Count current requests
const requestCount = await this.redis.zcard(key);
if (requestCount >= limit) {
return false;
}
// Add current request
await this.redis.zadd(key, now, `${now}-${Math.random()}`);
await this.redis.expire(key, Math.ceil(windowMs / 1000));
return true;
}
}
Request Transformation
Data Transformation
Request Enrichment:
Incoming Request ──► Add User Context ──► Service
│ │
└─► {userId: 123} ───────┘
Response Transformation:
Service Response ──► Remove Sensitive Data ──► Client
│ │
└─► Filter Fields ──────────┘
Protocol Translation
REST to GraphQL:
REST API ──► Gateway ──► GraphQL Schema ──► Resolvers
│ │ │
└──────────────┼──────────┘
Query Optimization
Caching Strategies
Response Caching
HTTP Caching Headers:
class CacheMiddleware {
async handleRequest(req: Request, res: Response): Promise<void> {
const cacheKey = this.generateCacheKey(req);
// Check cache
const cached = await this.cache.get(cacheKey);
if (cached && this.isCacheValid(cached, req)) {
res.setHeader('X-Cache', 'HIT');
res.send(cached.data);
return;
}
// Set cache headers for response
res.setHeader('Cache-Control', 'public, max-age=300');
res.setHeader('ETag', this.generateETag(res.body));
// Cache the response
await this.cache.set(cacheKey, {
data: res.body,
headers: res.headers,
timestamp: Date.now()
});
}
}
Distributed Caching
Cache Replication:
API Gateway ──► Redis Cluster ──► Cache Hit
│ │
└──────────────┘
Cache Miss → Origin
Circuit Breaker Pattern
Circuit Breaker States
State Machine:
Closed ──► Open (when failures > threshold)
▲ │
└─────────┼─► Half-Open (test request)
│
└─► Closed (if success)
Implementation:
enum CircuitState {
CLOSED = 'CLOSED',
OPEN = 'OPEN',
HALF_OPEN = 'HALF_OPEN'
}
class CircuitBreaker {
private state: CircuitState = CircuitState.CLOSED;
private failureCount = 0;
private lastFailureTime = 0;
async execute<T>(operation: () => Promise<T>): Promise<T> {
if (this.state === CircuitState.OPEN) {
if (this.shouldAttemptReset()) {
this.state = CircuitState.HALF_OPEN;
} else {
throw new CircuitBreakerError('Circuit is open');
}
}
try {
const result = await operation();
this.onSuccess();
return result;
} catch (error) {
this.onFailure();
throw error;
}
}
private onSuccess(): void {
this.failureCount = 0;
this.state = CircuitState.CLOSED;
}
private onFailure(): void {
this.failureCount++;
this.lastFailureTime = Date.now();
if (this.failureCount >= this.failureThreshold) {
this.state = CircuitState.OPEN;
}
}
}
Monitoring and Observability
Gateway Metrics
Key Metrics to Track:
- Request latency by endpoint
- Error rates by service
- Rate limiting hits
- Authentication failures
- Cache hit/miss ratios
Metrics Collection:
class MetricsCollector {
private metrics: Map<string, Metric> = new Map();
recordRequest(endpoint: string, method: string, statusCode: number, duration: number): void {
const key = `${method}:${endpoint}`;
this.metrics.set(key, {
count: (this.metrics.get(key)?.count || 0) + 1,
totalDuration: (this.metrics.get(key)?.totalDuration || 0) + duration,
statusCodes: {
...this.metrics.get(key)?.statusCodes,
[statusCode]: (this.metrics.get(key)?.statusCodes?.[statusCode] || 0) + 1
}
});
}
getMetrics(): GatewayMetrics {
return {
endpoints: Array.from(this.metrics.entries()).map(([key, metric]) => ({
endpoint: key,
requests: metric.count,
avgLatency: metric.totalDuration / metric.count,
statusCodes: metric.statusCodes
}))
};
}
}
Distributed Tracing
Request Tracing:
Client Request ──► Gateway (span-1) ──► Service A (span-2) ──► Service B (span-3)
│ │ │ │
└─ Trace ID: abc-123 ──┼─ Parent: span-1 ─────┼─ Parent: span-2 ─────┘
Service Orchestration
API Composition
Request Aggregation:
Gateway ──► Parallel Requests ──► Combine Results
│ ├─► Service A
│ ├─► Service B
│ └─► Service C
│
└─► Single Response
Saga Orchestration
Distributed Transaction Coordination:
Gateway ──► Start Saga ──► Service A ──► Service B ──► Complete
│ │ │ │
└─────────┼──────────────┼──────────────┘
Compensate on Failure
Security Best Practices
Input Validation and Sanitization
Request Validation:
class ValidationMiddleware {
async validateRequest(req: Request): Promise<ValidationResult> {
const schema = this.getSchema(req.path);
try {
const validated = await schema.validate(req.body);
return { valid: true, data: validated };
} catch (error) {
return {
valid: false,
errors: error.errors.map(err => ({
field: err.field,
message: err.message
}))
};
}
}
}
CORS Configuration
Cross-Origin Resource Sharing:
const corsOptions = {
origin: (origin, callback) => {
const allowedOrigins = process.env.ALLOWED_ORIGINS?.split(',') || [];
if (allowedOrigins.includes(origin) || !origin) {
callback(null, true);
} else {
callback(new Error('Not allowed by CORS'));
}
},
credentials: true,
methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],
allowedHeaders: ['Content-Type', 'Authorization', 'X-API-Key']
};
Performance Optimization
Connection Pooling
HTTP Client Configuration:
const httpClient = axios.create({
baseURL: 'http://service-registry',
timeout: 5000,
httpAgent: new http.Agent({
keepAlive: true,
maxSockets: 100,
maxFreeSockets: 10,
timeout: 60000
})
});
Response Compression
Gzip Compression:
gzip on;
gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;
gzip_min_length 1024;
gzip_comp_level 6;
GitScrum Integration
Gateway Monitoring Dashboard
API Gateway Health Tracking:
- Monitor request throughput and latency
- Track service availability and error rates
- Set up alerts for gateway failures
Service Dependency Mapping
Dependency Visualization:
API Gateway ──► User Service ──► Auth Service
│ │ │
└──────────────┼───────────────┘
Service Mesh Integration