Patron de Enrutamiento de Gateway
Enruta solicitudes a multiples servicios backend a traves de un unico punto de entrada que gestiona preocupaciones transversales.
Nota para desarrolladores hispanohablantes: Esta guía incluye ejemplos y convenciones de nomenclatura adaptadas a equipos que trabajan en español. Cuando existen diferencias significativas en terminología técnica entre el inglés y el español, se indican explícitamente para facilitar la comunicación en equipos multiculturales.
Visión General
El Patron de Enrutamiento de Gateway coloca un unico punto de entrada frente a multiples servicios backend. En lugar de exponer cada servicio directamente a los clientes, el gateway recibe las solicitudes y las enruta al upstream adecuado segun la ruta, el metodo, los encabezados u otras reglas. Tambien centraliza preocupaciones transversales como la terminacion TLS, la autenticacion, el rate limiting y el registro.
Este patron es esencial para arquitecturas de microservicios y modulares donde deseas un contrato externo limpio mientras permites que los servicios internos evolucionen de forma independiente.
Cuándo Usar
- For alternatives, see API Gateway Design: Resilience, Routing, and Security.
Usa este patron cuando:
- Tengas multiples servicios backend que los clientes deben alcanzar a traves de una sola direccion
- Necesites aplicar TLS, autenticacion o rate limiting en un solo lugar
- Quieras enrutar trafico por ruta URL, host o version de API sin cambiar clientes
- Estes migrando de un monolito a microservicios y necesites ocultar cambios internos
- Necesites componer respuestas de varios servicios o aplicar traduccion de protocolos
Solución
// Configuracion simplificada de rutas de gateway estilo Express
import express from 'express';
import { createProxyMiddleware } from 'http-proxy-middleware';
const app = express();
app.use('/users', createProxyMiddleware({
target: 'http://users-service:3001',
changeOrigin: true,
}));
app.use('/orders', createProxyMiddleware({
target: 'http://orders-service:3002',
changeOrigin: true,
}));
app.use('/inventory', createProxyMiddleware({
target: 'http://inventory-service:3003',
changeOrigin: true,
}));
app.listen(3000, () => console.log('Gateway escuchando en el puerto 3000'));
# Ejemplo de enrutamiento basado en ubicaciones en NGINX
server {
listen 443 ssl;
server_name api.example.com;
location /users {
proxy_pass http://users-service;
}
location /orders {
proxy_pass http://orders-service;
}
location /inventory {
proxy_pass http://inventory-service;
}
}
Explicación
El Patron de Enrutamiento de Gateway funciona insertando un proxy inverso o gateway dedicado entre clientes y servicios. El gateway mantiene una tabla de enrutamiento que mapea caracteristicas de las solicitudes entrantes a destinos upstream. Cuando llega una solicitud, el gateway la compara con la tabla, aplica cualquier middleware y la reenvia. Las respuestas viajan de regreso a traves del gateway, que puede transformar encabezados o cachear resultados.
Responsabilidades clave del gateway:
- Enrutamiento: emparejar solicitudes con servicios segun ruta, host, encabezados o version
- Balanceo de carga: distribuir solicitudes entre instancias upstream saludables
- Seguridad: terminar TLS, validar tokens y aplicar rate limits
- Observabilidad: recolectar metricas y logs de todo el trafico
Variantes
| Variante | Caso de Uso | Compromiso |
|---|---|---|
| API Gateway | Exponer APIs publicas a clientes externos | Centralizado pero puede convertirse en cuello de botella |
| Backend for Frontend | Adaptar APIs para web, movil o clientes socios | Agrega un servicio por tipo de cliente |
| Edge Gateway | Gestionar TLS, DDoS y cache en el borde de red | Simplifica origenes pero agrega dependencia de proveedor |
| Gateway Interno | Enrutar trafico dentro de un cluster con mTLS | Mantiene el trafico privado y seguro |
Lo que Funciona
- Manten el gateway sin estado para que pueda escalar horizontalmente
- Almacena las reglas de enrutamiento en configuracion en lugar de codificarlas
- Usa health checks para evitar enrutar a servicios upstream fallidos
- Descarga TLS en el gateway para reducir la complejidad de certificados en los servicios
- Limita la logica del gateway a preocupaciones transversales; evita logica de negocio
- Registra IDs de solicitud y IDs de correlacion para trazabilidad distribuida
Errores Comunes
- Colocar logica de negocio en el gateway, dificultando su mantenimiento
- Enrutar cada microservicio a traves de un unico gateway sin escalarlo
- Ignorar la configuracion de timeout y reintentos, causando fallos en cascada
- Olvidar validar certificados TLS en conexiones upstream
- Enrutar basandose en reglas fragiles como cadenas de consulta que cambian frecuentemente
Soluciones Avanzadas
Enrutamiento dinamico con descubrimiento de servicios
Integra el enrutamiento de gateway con descubrimiento de servicios para actualizaciones automaticas de upstream:
import { ServiceRegistry } from './service-registry';
import { createProxyMiddleware } from 'http-proxy-middleware';
class DynamicGateway {
private registry: ServiceRegistry;
private app: express.Application;
constructor(registry: ServiceRegistry) {
this.registry = registry;
this.app = express();
this.setupRoutes();
}
async setupRoutes() {
const services = await this.registry.getAllServices();
services.forEach(service => {
const targets = service.instances.map(
instance => `${instance.host}:${instance.port}`
);
this.app.use(service.path, createProxyMiddleware({
target: `http://${targets[0]}`,
changeOrigin: true,
router: (req) => {
// Balancear carga entre instancias saludables
const healthyInstances = service.instances.filter(i => i.healthy);
const selected = healthyInstances[Math.floor(Math.random() * healthyInstances.length)];
return `${selected.host}:${selected.port}`;
},
onProxyReq: (proxyReq, req, res) => {
proxyReq.setHeader('X-Request-ID', req.id);
},
onError: (err, req, res) => {
console.error(`Error de proxy: ${err.message}`);
res.status(502).json({ error: 'Bad Gateway' });
}
}));
});
}
listen(port: number) {
this.app.listen(port, () => console.log(`Gateway escuchando en ${port}`));
}
}
Integracion de circuit breaker
Agrega el patron de circuit breaker para prevenir fallos en cascada:
import CircuitBreaker from 'opossum';
const options = {
timeout: 3000,
errorThresholdPercentage: 50,
resetTimeout: 30000
};
class CircuitBreakerGateway {
private breakers: Map<string, any>;
constructor() {
this.breakers = new Map();
}
getBreaker(serviceName: string) {
if (!this.breakers.has(serviceName)) {
const breaker = new CircuitBreaker(
async (url: string, options: RequestInit) => {
const response = await fetch(url, options);
if (!response.ok) throw new Error(`HTTP ${response.status}`);
return response.json();
},
options
);
breaker.on('open', () => console.log(`Circuito abierto para ${serviceName}`));
breaker.on('halfOpen', () => console.log(`Circulo medio-abierto para ${serviceName}`));
breaker.on('close', () => console.log(`Circulo cerrado para ${serviceName}`));
this.breakers.set(serviceName, breaker);
}
return this.breakers.get(serviceName);
}
async proxyRequest(serviceName: string, path: string, request: Request) {
const breaker = this.getBreaker(serviceName);
const serviceUrl = `http://${serviceName}:3001${path}`;
return breaker.fire(serviceUrl, {
method: request.method,
headers: request.headers,
body: request.body
});
}
}
Middleware de transformacion de solicitudes
Transforma solicitudes y respuestas en el gateway:
class TransformGateway {
private app: express.Application;
constructor() {
this.app = express();
this.setupTransforms();
}
setupTransforms() {
// Transformar encabezados de solicitud
this.app.use('/api/v1', (req, res, next) => {
req.headers['x-api-version'] = 'v1';
req.headers['x-request-time'] = new Date().toISOString();
next();
});
// Transformar formato de respuesta
this.app.use('/api/v2', async (req, res, next) => {
const originalJson = res.json;
res.json = function(data) {
const transformed = {
meta: {
version: 'v2',
timestamp: new Date().toISOString()
},
data: data
};
originalJson.call(this, transformed);
};
next();
});
// Traduccion de protocolo (REST a gRPC)
this.app.post('/grpc-proxy', async (req, res) => {
const grpcClient = loadGrpcClient('users-service');
const grpcRequest = mapRestToGrpc(req.body);
try {
const grpcResponse = await grpcClient.getUser(grpcRequest);
const restResponse = mapGrpcToRest(grpcResponse);
res.json(restResponse);
} catch (error) {
res.status(500).json({ error: 'Fallo de traduccion gRPC' });
}
});
}
}
Mejores Practicas Adicionales
-
Implementa transformacion de solicitud/respuesta en el gateway. Usa middleware para normalizar versiones de API, transformar formatos de datos y manejar traduccion de protocolos. Esto mantiene los servicios backend simples y consistentes.
-
Usa enrutamiento ponderado para despliegues canary. Enruta un porcentaje de trafico a una nueva version de un servicio para rollout gradual. Monitorea metricas y haz rollback automatico si los errores aumentan.
# Configuracion de enrutamiento ponderado
routes:
- path: /api/users
upstreams:
- service: users-service-v1
weight: 90 # 90% del trafico
- service: users-service-v2
weight: 10 # 10% del trafico (canary)
- Implementa rate limiting por cliente. Usa rate limiting basado en IP, clave de API o usuario para prevenir abuso. Almacena contadores de rate limit en Redis para gateways distribuidos.
Preguntas frecuentes
- ¿Es este patrón adecuado para proyectos pequeños?
Para proyectos pequeños con pocos componentes, este patrón puede añadir complejidad innecesaria. Empieza simple e introduce el patrón cuando sientas el problema que resuelve.
- ¿Cómo se compara este patrón con alternativas?
Cada patrón hace diferentes trade-offs. Revisa la tabla de variantes arriba y considera tus restricciones específicas: tamaño del equipo, requisitos de rendimiento y planes de escalado.
- ¿Puedo aplicar este patrón parcialmente?
Sí. Muchos equipos adoptan patrones incrementalmente. Empieza con la idea central y añade sofisticación según sea necesario. El patrón es una guía, no un blueprint estricto.
Recursos Relacionados
Diseño de API Gateway: Resiliencia, Enrutamiento y Seguridad
Guía práctica para diseñar API gateways: patrones de enrutamiento, rate limiting, autenticación, circuit breakers y observabilidad para APIs resilientes.
PatternPatrón Anti-Corruption Layer
Cómo isolatar legacy systems con translation adapters. Cubre ACL facade, domain translation, bidirectional mapping, y gradual legacy replacement.
PatternPatrón Backend for Frontend (BFF)
Crea servicios backend dedicados adaptados a las necesidades específicas de cada tipo de frontend client, agregando APIs downstream y optimizando formas de datos por plataforma.