APIs y contratos

Diseño de APIs, comparación entre REST, GraphQL y gRPC, versionado de APIs y el enfoque contract-first.

Comunicación entre componentes

En una arquitectura moderna, los componentes no solo necesitan existir; necesitan comunicarse de forma clara, estable y comprensible. Ahí entran las APIs y los contratos.

Las decisiones sobre APIs afectan directamente la mantenibilidad, escalabilidad y experiencia de desarrollo de todo el sistema.

¿Qué es una API?

Una API es una interfaz que permite que un sistema o componente exponga capacidades a otros.

En términos simples, una API define:

  • Qué puedes pedir
  • Cómo debes pedirlo
  • Qué respuesta puedes esperar
  • Qué errores pueden ocurrir

En cualquier sistema distribuido, las APIs son el mecanismo de comunicación entre componentes. Una API bien diseñada actúa como un contrato: define qué puede pedir un consumidor, qué va a recibir y bajo qué condiciones.

REST (Representational State Transfer)

REST es el estilo arquitectónico más utilizado para APIs web. Se basa en recursos identificados por URLs y operaciones estándar HTTP.

Principios fundamentales

  • Recursos: Todo es un recurso identificado por una URI (/usuarios/123).
  • Verbos HTTP: GET (leer), POST (crear), PUT (reemplazar), PATCH (actualizar parcial), DELETE (eliminar).
  • Sin estado: Cada petición contiene toda la información necesaria; el servidor no guarda estado de sesión.
  • Representaciones: Un recurso puede tener múltiples representaciones (JSON, XML).

Ejemplo de API REST

GET    /api/pedidos          → Lista de pedidos
GET    /api/pedidos/42       → Detalle del pedido 42
POST   /api/pedidos          → Crear nuevo pedido
PATCH  /api/pedidos/42       → Actualizar pedido 42
DELETE /api/pedidos/42       → Eliminar pedido 42

Cuándo resulta útil

  • APIs simples o medianas
  • Integración entre servicios
  • Interfaces públicas o privadas bien definidas
  • Cuando necesitas cacheo HTTP nativo

Ventajas de REST

  • Ampliamente adoptado y comprendido por la industria.
  • Cacheable de forma nativa con headers HTTP.
  • Herramientas maduras para documentación (OpenAPI/Swagger).
  • Funciona bien con infraestructura web existente (proxies, CDNs, load balancers).

Desventajas de REST

  • Over-fetching: El endpoint devuelve más datos de los que el cliente necesita.
  • Under-fetching: El cliente necesita hacer múltiples peticiones para obtener datos relacionados.
  • Sin tipado nativo; depende de documentación externa para definir la estructura.

GraphQL

GraphQL es un lenguaje de consulta para APIs que permite al cliente especificar exactamente qué datos necesita.

Cómo funciona

# El cliente pide exactamente lo que necesita
query {
  pedido(id: 42) {
    id
    fecha
    total
    cliente {
      nombre
      email
    }
    items {
      producto
      cantidad
    }
  }
}

Ventajas de GraphQL

  • Sin over-fetching ni under-fetching: El cliente define la forma de la respuesta.
  • Un solo endpoint: Todas las consultas van a /graphql.
  • Tipado fuerte: El schema define tipos, campos y relaciones de forma explícita.
  • Introspección: El cliente puede descubrir el schema disponible en tiempo de ejecución.
  • Muy adecuado en BFFs para agregación de datos.

Costes de GraphQL

  • Complejidad en el servidor: Resolver queries anidadas puede generar problemas de rendimiento (N+1 queries).
  • Caché difícil: Al no usar URLs únicas por recurso, el caché HTTP estándar no aplica directamente.
  • Curva de aprendizaje: Requiere entender un nuevo paradigma tanto en cliente como en servidor.
  • Seguridad: Queries complejas pueden ser usadas para ataques de denegación de servicio si no se limitan.
  • Control más delicado de performance y observabilidad algo distinta a REST.

gRPC

gRPC es un framework de comunicación de alto rendimiento que usa Protocol Buffers (protobuf) para serialización y HTTP/2 como transporte.

Definición del servicio

syntax = "proto3";

service ServicioPedidos {
  rpc ObtenerPedido (PedidoRequest) returns (PedidoResponse);
  rpc CrearPedido (CrearPedidoRequest) returns (PedidoResponse);
  rpc ListarPedidos (ListarRequest) returns (stream PedidoResponse);
}

message PedidoRequest {
  int32 id = 1;
}

message PedidoResponse {
  int32 id = 1;
  string fecha = 2;
  double total = 3;
}

Ventajas de gRPC

  • Alto rendimiento: Serialización binaria (protobuf) es más rápida y compacta que JSON.
  • Streaming bidireccional: Soporta comunicación en tiempo real con HTTP/2.
  • Generación de código: A partir del archivo .proto, se generan clientes y servidores en múltiples lenguajes.
  • Contratos estrictos: El schema protobuf es el contrato; no hay ambigüedad.

Desventajas de gRPC

  • No funciona en navegadores directamente (necesita gRPC-Web como proxy).
  • Depuración difícil: Los mensajes binarios no son legibles sin herramientas especiales.
  • Menor adopción: Menos herramientas y documentación comparado con REST.
  • Complejidad de setup: Requiere compilador de protobuf y configuración adicional.

Comparación directa

CriterioRESTGraphQLgRPC
Formato de datosJSON (texto)JSON (texto)Protobuf (binario)
TransporteHTTP/1.1 o HTTP/2HTTP/1.1 o HTTP/2HTTP/2
TipadoExterno (OpenAPI)Nativo (schema)Nativo (protobuf)
Caso de uso idealAPIs públicas, CRUDFrontends con datos complejosComunicación entre servicios
RendimientoBuenoVariableExcelente
CachéNativo HTTPRequiere solución customNo estándar
Curva de aprendizajeBajaMediaMedia-Alta

Qué es un contrato

Un contrato es el acuerdo entre productor y consumidor. Puede definir:

  • Estructura de request y response
  • Campos obligatorios y opcionales
  • Tipos de datos
  • Errores posibles
  • Headers requeridos
  • Reglas de versionado

Un contrato reduce ambigüedad y ayuda a evolucionar sistemas sin romper consumidores.

Contratos en eventos

No solo las APIs tienen contratos. Los eventos también necesitan esquemas claros.

Por ejemplo, si un servicio publica OrderPlaced, otros servicios necesitan saber:

  • Qué campos contiene
  • Qué significan
  • Cuáles son obligatorios
  • Cómo evoluciona el schema

Herramientas de contrato por tecnología

TecnologíaHerramienta de contrato
RESTOpenAPI (Swagger)
GraphQLSDL (Schema Definition Language)
gRPCProtocol Buffers (.proto)
EventosAsyncAPI

Diseño Contract-First

El enfoque contract-first (contrato primero) propone definir la API antes de implementarla. En lugar de escribir código y generar documentación después, defines el contrato y luego implementas contra él.

Flujo contract-first

1. Definir el contrato (OpenAPI, protobuf, GraphQL schema)

2. Revisar y validar con consumidores

3. Generar código base (servidor y cliente)

4. Implementar la lógica de negocio

5. Validar que la implementación cumple el contrato

Ventajas del contract-first

  • Desarrollo paralelo: Frontend y backend pueden trabajar simultáneamente usando el contrato como referencia.
  • Documentación siempre actualizada: El contrato es la documentación.
  • Validación automática: Herramientas pueden verificar que la implementación cumple el contrato.
  • Mejor comunicación: El contrato es un artefacto compartido entre equipos.

Evolución y versionado

Los contratos cambian con el tiempo. Por eso es importante pensar en:

  • Compatibilidad hacia atrás: Los cambios no deben romper a los consumidores existentes.
  • Deprecación gradual: Marca endpoints o campos como deprecated y da tiempo para migrar.
  • Cambios breaking: Identifica cuándo un cambio es incompatible y requiere una nueva versión.
  • Ownership del contrato: Define quién es responsable de mantener y evolucionar cada contrato.

Estrategias comunes de versionado

Versionado por URL

GET /api/v1/pedidos
GET /api/v2/pedidos

Es la estrategia más simple y explícita. El consumidor sabe exactamente qué versión está usando.

Versionado por header

GET /api/pedidos
Accept: application/vnd.miapi.v2+json

Mantiene las URLs limpias pero es menos visible y más difícil de probar en el navegador.

Versionado por query parameter

GET /api/pedidos?version=2

Simple pero puede interferir con el caché y no es considerada una buena práctica por muchos equipos.

Buenas prácticas de versionado

  • Evita romper cambios: Agrega campos nuevos en lugar de modificar los existentes.
  • Depreca antes de eliminar: Marca endpoints como deprecated y da tiempo a los consumidores para migrar.
  • Documenta los cambios: Mantén un changelog claro entre versiones.
  • Limita versiones activas: No mantengas más de 2-3 versiones simultáneas.

Por qué importan los contratos

Sin contratos claros:

  • Los equipos interpretan cosas distintas
  • Aparecen integraciones frágiles
  • Los cambios rompen consumidores
  • Crece el acoplamiento accidental

Con contratos bien definidos:

  • Hay menos ambigüedad
  • La evolución se vuelve más segura
  • Los equipos pueden coordinarse mejor

Principios de buen diseño de APIs

Independientemente de la tecnología elegida, estas prácticas aplican a cualquier API:

  • Consistencia: Usa convenciones uniformes en nombres, formatos y códigos de error.
  • Idempotencia: Las operaciones GET, PUT y DELETE deben producir el mismo resultado si se ejecutan múltiples veces.
  • Manejo de errores claro: Devuelve códigos de estado apropiados y mensajes de error útiles.
  • Paginación: Nunca devuelvas colecciones completas sin límite; implementa paginación desde el inicio.
  • Seguridad: Autenticación (quién eres) y autorización (qué puedes hacer) en cada endpoint.

¿Cuál elegir?

  • REST si necesitas una API pública, simple y ampliamente compatible.
  • GraphQL si tu frontend necesita flexibilidad para consultar datos complejos y relacionados.
  • gRPC si necesitas comunicación de alto rendimiento entre servicios internos.

En muchos sistemas reales, coexisten las tres tecnologías: REST para APIs públicas, GraphQL para el BFF que alimenta al frontend, y gRPC para comunicación interna entre microservicios.

Resumen

Las APIs permiten comunicación entre componentes. Los contratos hacen esa comunicación explícita, mantenible y evolutiva. En sistemas distribuidos, pensar bien los contratos es tan importante como escribir el código que los implementa.