Patrones y Estrategias de Implementación de API Gateway
Los API gateways sirven como el único punto de entrada para que las aplicaciones cliente accedan a microservicios, proporcionando preocupaciones transversales esenciales como autenticación, limitación de tasa, enrutamiento y monitoreo. Comprender los patrones de gateway es crucial para construir sistemas distribuidos escalables y seguros.
Arquitectura Central de API Gateway
Ubicación de Gateway en Microservicios
API Gateway como Único Punto de Entrada:
Internet ──► API Gateway ──► Service Registry
│ │ │
▼ ▼ ▼
Clients ──────────┼───────────────┼─► Microservicio A
│ │
└───────────────┼─► Microservicio B
│
└─► Microservicio C
Responsabilidades del Gateway:
- Enrutamiento de Solicitudes: Dirigir solicitudes a servicios apropiados
- Autenticación: Validar credenciales y permisos de usuario
- Limitación de Tasa: Controlar tasas de solicitud para prevenir abuso
- Balanceo de Carga: Distribuir solicitudes entre instancias de servicio
- Monitoreo: Rastrear métricas de solicitud y salud
Patrones de Enrutamiento
Enrutamiento Basado en Ruta
Mapeo de Ruta URL:
/api/v1/users/* ──► User Service
/api/v1/orders/* ──► Order Service
/api/v1/products/* ──► Product Service
Ejemplo de Implementación:
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;
}
Enrutamiento Basado en Encabezado
Enrutamiento por Content-Type:
Content-Type: application/json ──► JSON Service
Content-Type: application/xml ──► XML Service
Enrutamiento Basado en Versión:
X-API-Version: v1 ──► Legacy Service
X-API-Version: v2 ──► Modern Service
Enrutamiento Dinámico con Descubrimiento de Servicio
Integración con Service Registry:
API Gateway ──► Consul/Eureka ──► Service Instances
│ │
└─► Health Check ──► Load Balance ──► Route Request
Autenticación y Autorización
Validación de Token JWT
Flujo JWT:
Client ──► JWT Token ──► Gateway ──► Validate Token ──► Service
▲ │ │
└──────────────────────┴───────────────┘
Token Refresh
Implementación de Validación de Token:
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));
}
}
Integración OAuth2
Flujo de Código de Autorización:
Client ──► Gateway ──► Auth Server ──► User Consent ──► Tokens
▲ │ │ │
└──────────────┼────────────────────┼────────────────────┘
Redirect with Code ──► Exchange for Tokens
Autenticación por Clave API
Acceso Basado en Clave:
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' };
}
}
Patrones de Limitación de Tasa
Algoritmo Token Bucket
Implementación Token Bucket:
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;
}
}
Contador de Ventana Deslizante
Limitación de Tasa Distribuida:
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;
}
}
Transformación de Solicitudes
Transformación de Datos
Enriquecimiento de Solicitud:
Incoming Request ──► Add User Context ──► Service
│ │
└─► {userId: 123} ───────┘
Transformación de Respuesta:
Service Response ──► Remove Sensitive Data ──► Client
│ │
└─► Filter Fields ──────────┘
Traducción de Protocolo
REST a GraphQL:
REST API ──► Gateway ──► GraphQL Schema ──► Resolvers
│ │ │
└──────────────┼──────────┘
Query Optimization
Estrategias de Cache
Cache de Respuesta
Encabezados de Cache HTTP:
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()
});
}
}
Cache Distribuido
Replicación de Cache:
API Gateway ──► Redis Cluster ──► Cache Hit
│ │
└──────────────┘
Cache Miss → Origin
Patrón Circuit Breaker
Estados del Circuit Breaker
Máquina de Estados:
Closed ──► Open (when failures > threshold)
▲ │
└─────────┼─► Half-Open (test request)
│
└─► Closed (if success)
Implementación:
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;
}
}
}
Monitoreo y Observabilidad
Métricas de Gateway
Métricas Principales a Rastrear:
- Latencia de solicitud por endpoint
- Tasas de error por servicio
- Aciertos de limitación de tasa
- Fallos de autenticación
- Tasas de acierto/fallo de cache
Colección de Métricas:
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
}))
};
}
}
Rastreo Distribuido
Rastreo de Solicitud:
Client Request ──► Gateway (span-1) ──► Service A (span-2) ──► Service B (span-3)
│ │ │ │
└─ Trace ID: abc-123 ──┼─ Parent: span-1 ─────┼─ Parent: span-2 ─────┘
Orquestación de Servicio
Composición de API
Agregación de Solicitudes:
Gateway ──► Parallel Requests ──► Combine Results
│ ├─► Service A
│ ├─► Service B
│ └─► Service C
│
└─► Single Response
Orquestación Saga
Coordinación de Transacción Distribuida:
Gateway ──► Start Saga ──► Service A ──► Service B ──► Complete
│ │ │ │
└─────────┼──────────────┼──────────────┘
Compensate on Failure
Mejores Prácticas de Seguridad
Validación y Sanitización de Entrada
Validación de Solicitud:
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
}))
};
}
}
}
Configuración CORS
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']
};
Optimización de Rendimiento
Pooling de Conexiones
Configuración de Cliente HTTP:
const httpClient = axios.create({
baseURL: 'http://service-registry',
timeout: 5000,
httpAgent: new http.Agent({
keepAlive: true,
maxSockets: 100,
maxFreeSockets: 10,
timeout: 60000
})
});
Compresión de Respuesta
Compresión Gzip:
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;
Integración con GitScrum
Dashboard de Monitoreo de Gateway
Rastreo de Salud de API Gateway:
- Monitorear throughput y latencia de solicitud
- Rastrear disponibilidad de servicio y tasas de error
- Configurar alertas para fallos de gateway
Mapeo de Dependencia de Servicio
Visualización de Dependencia:
API Gateway ──► User Service ──► Auth Service
│ │ │
└──────────────┼───────────────┘
Service Mesh Integration