Automation · APIs|12 min de lectura|

Integraciones API Empresariales: Diseño, Seguridad y Resiliencia

Las integraciones entre sistemas son el tejido conectivo de una empresa moderna. También son la fuente más frecuente de incidentes difíciles de diagnosticar: un tercero que cambia su API sin aviso, una integración que falla silenciosamente durante horas, o un webhook que procesa el mismo evento dos veces con consecuencias financieras. Esta guía documenta los patrones de integración que hacen la diferencia entre sistemas que se caen juntos y sistemas que fallan de forma elegante.

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.
yaml
# 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: false

OAuth2 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.

typescript
// 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.

typescript
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' });
});
El circuit breaker fallback no debe simular éxito silenciosamente. Si el pago falló porque el circuito está abierto, el usuario debe saberlo. El fallback correcto comunica degradación y permite al usuario o al sistema reintentar conscientemente.

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.

typescript
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.

typescript
// 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?
REST para APIs públicas y para integraciones con terceros — es el estándar que todos entienden. gRPC para comunicación interna entre microservicios en producción — más eficiente en serialización (Protocol Buffers), soporte nativo de streaming, y tipado estricto con generación de código. GraphQL cuando el cliente necesita flexibilidad sobre qué campos obtener — evitar para APIs internas simples donde la complejidad no se justifica.
¿Cómo gestiono versiones de API sin romper las integraciones existentes?
El enfoque más pragmático: versioning en la URL (/api/v1/, /api/v2/). Las versiones anteriores se mantienen activas durante un período de deprecación anunciado (mínimo 6 meses para APIs empresariales). Los breaking changes siempre van en una versión nueva. Los campos opcionales adicionales y nuevos endpoints no son breaking changes y pueden ir en la versión existente.
¿Cómo detecto cuando una integración de terceros está fallando silenciosamente?
La combinación correcta: alertas sobre la tasa de error y latencia de cada integración en Prometheus/Datadog, dead letter queues para mensajes que no se pudieron procesar, y alertas sobre el volumen de webhooks recibidos (si el volumen baja 80% respecto al promedio, algo está mal en el proveedor). La detección de fallos silenciosos requiere monitoreo activo, no solo reaccionar a errores explícitos.
¿Cómo documento APIs internas de forma que no se desactualice?
OpenAPI (Swagger) generado automáticamente desde el código, no escrito manualmente. Frameworks como NestJS, FastAPI y Spring Boot pueden generar el spec OpenAPI desde las anotaciones del código. Esto garantiza que la documentación siempre refleja la implementación actual. Herramientas como Stoplight o Redocly pueden publicar la documentación automáticamente desde el pipeline CI.
¿Qué es un API contract test y cuándo es necesario?
Un contract test verifica que una API (tuya o de terceros) cumple el contrato que tus integraciones esperan. Pact es la herramienta más usada. Son especialmente valiosos cuando múltiples equipos consumen la misma API interna: el contract test detecta si un cambio en el proveedor rompería a los consumidores, antes de deployar a producción.

¿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

Artículos relacionados

IQS

Equipo de Ingeniería — IQS

Ingenieros de software, cloud y DevOps con experiencia en proyectos empresariales.