API Gateway: el punto de entrada único
Un API Gateway centraliza las preocupaciones transversales de todas las APIs: autenticación, rate limiting, logging, transformación de requests, y routing. Elimina la necesidad de implementar estas funciones en cada microservicio individualmente.
- Kong (open-source): la opción más flexible para self-hosted. Plugins para autenticación JWT, OAuth2, rate limiting, CORS, y transformación de requests. Desplegable en Kubernetes con el ingress controller de Kong.
- AWS API Gateway: integración nativa con Lambda, Cognito y IAM. Ideal si el backend es principalmente serverless o ya hay fuerte dependencia de AWS.
- Traefik: si ya usas Traefik como ingress controller en Kubernetes, sus middlewares cubren la mayoría de los casos: autenticación básica/JWT, rate limiting, circuit breaker.
# Kong — Plugin de rate limiting por consumer
apiVersion: configuration.konghq.com/v1
kind: KongPlugin
metadata:
name: rate-limit-api
plugin: rate-limiting
config:
minute: 100
hour: 1000
policy: redis
redis_host: redis.infrastructure.svc.cluster.local
redis_port: 6379
---
# Plugin de autenticación JWT
apiVersion: configuration.konghq.com/v1
kind: KongPlugin
metadata:
name: jwt-auth
plugin: jwt
config:
key_claim_name: iss
secret_is_base64: falseOAuth2 y JWT: autenticación correcta para APIs empresariales
El error más común en autenticación de APIs: usar API keys estáticas sin expiración ni rotación. Para APIs empresariales que integran sistemas internos y terceros, OAuth2 con JWT tokens de corta duración es el estándar correcto.
// Validación de JWT con verificación de claims empresariales
import jwt from 'jsonwebtoken';
import jwksClient from 'jwks-rsa';
const client = jwksClient({
jwksUri: 'https://auth.empresa.com/.well-known/jwks.json',
cache: true,
cacheMaxAge: 86400000, // 24h cache para las keys públicas
});
async function validateToken(token: string): Promise<JwtPayload> {
const decoded = jwt.decode(token, { complete: true });
if (!decoded?.header?.kid) throw new Error('Token inválido: sin kid');
const key = await client.getSigningKey(decoded.header.kid);
const publicKey = key.getPublicKey();
return jwt.verify(token, publicKey, {
algorithms: ['RS256'],
audience: 'api.empresa.com', // verificar audience
issuer: 'https://auth.empresa.com', // verificar issuer
}) as JwtPayload;
}Circuit Breaker: aislar fallos de terceros
Cuando un servicio externo (proveedor de pagos, API de geolocalización, ERP) empieza a fallar o a responder lentamente, sin un circuit breaker ese comportamiento se propaga a tu sistema: tu API espera la respuesta del tercero mientras acumula conexiones abiertas hasta que se agota el pool de threads y tu servicio también cae.
import CircuitBreaker from 'opossum';
const options = {
timeout: 3000, // si no responde en 3s, cuenta como fallo
errorThresholdPercentage: 50, // abrir el circuito al 50% de errores
resetTimeout: 30000, // intentar cerrar el circuito a los 30s
volumeThreshold: 5, // mínimo 5 requests antes de evaluar
};
const breaker = new CircuitBreaker(callPaymentProvider, options);
breaker.fallback(() => ({
status: 'pending',
message: 'Proveedor de pagos no disponible, reintentando automáticamente'
}));
breaker.on('open', () => {
logger.warn('Circuit breaker abierto: proveedor de pagos degradado');
metrics.increment('circuit_breaker.open', { service: 'payment-provider' });
});Retry con Backoff Exponencial y Jitter
El retry naïve (reintentar inmediatamente N veces) puede empeorar la situación de un servicio degradado al bombardearlo con el mismo volumen de requests. Backoff exponencial con jitter distribuye los reintentos en el tiempo, dando espacio al servicio destino para recuperarse.
async function retryWithBackoff<T>(
fn: () => Promise<T>,
maxAttempts = 4,
baseDelay = 1000
): Promise<T> {
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
try {
return await fn();
} catch (error) {
if (attempt === maxAttempts) throw error;
// No reintentar errores 4xx (son errores del cliente, no transitorios)
if (error instanceof ApiError && error.status >= 400 && error.status < 500) throw error;
// Backoff exponencial con jitter
const delay = Math.min(
baseDelay * Math.pow(2, attempt - 1) + Math.random() * 1000,
30000 // máximo 30 segundos
);
await new Promise(resolve => setTimeout(resolve, delay));
}
}
throw new Error('Unreachable');
}Webhooks confiables: idempotencia y verificación de firma
Los webhooks son el mecanismo por el que los sistemas externos te notifican de eventos (pago completado, factura emitida, estado de envío actualizado). El problema más frecuente: el mismo evento se entrega dos veces, y tu sistema lo procesa dos veces — con consecuencias que pueden ser financieras.
// Webhook handler con verificación de firma y idempotencia
app.post('/webhooks/payment', async (req, res) => {
// 1. Verificar firma HMAC del proveedor
const signature = req.headers['x-webhook-signature'] as string;
const payload = req.body;
const expectedSig = crypto
.createHmac('sha256', process.env.WEBHOOK_SECRET!)
.update(JSON.stringify(payload))
.digest('hex');
if (!crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expectedSig))) {
return res.status(401).json({ error: 'Firma inválida' });
}
// 2. Idempotencia: ignorar si ya se procesó este evento
const { event_id } = payload;
const existing = await db.processedEvents.findUnique({ where: { event_id } });
if (existing) return res.status(200).json({ status: 'already_processed' });
// 3. Procesar el evento
await processPaymentEvent(payload);
// 4. Marcar como procesado
await db.processedEvents.create({ data: { event_id, processed_at: new Date() } });
res.status(200).json({ status: 'ok' });
});Preguntas frecuentes
¿REST, GraphQL o gRPC para APIs empresariales internas?
¿Cómo gestiono versiones de API sin romper las integraciones existentes?
¿Cómo detecto cuando una integración de terceros está fallando silenciosamente?
¿Cómo documento APIs internas de forma que no se desactualice?
¿Qué es un API contract test y cuándo es necesario?
¿Tu empresa tiene integraciones frágiles que generan incidentes frecuentes? Podemos rediseñar las integraciones con los patrones correctos de resiliencia.
Habla con el equipo