API Gateway Patterns | Microservices Routing & Security
Implement API gateways for microservices with routing, authentication, and rate limiting. Kong, AWS API Gateway patterns for scalable distributed systems.
8 min read
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
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