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
| Criterio | REST | GraphQL | gRPC |
|---|---|---|---|
| Formato de datos | JSON (texto) | JSON (texto) | Protobuf (binario) |
| Transporte | HTTP/1.1 o HTTP/2 | HTTP/1.1 o HTTP/2 | HTTP/2 |
| Tipado | Externo (OpenAPI) | Nativo (schema) | Nativo (protobuf) |
| Caso de uso ideal | APIs públicas, CRUD | Frontends con datos complejos | Comunicación entre servicios |
| Rendimiento | Bueno | Variable | Excelente |
| Caché | Nativo HTTP | Requiere solución custom | No estándar |
| Curva de aprendizaje | Baja | Media | Media-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ía | Herramienta de contrato |
|---|---|
| REST | OpenAPI (Swagger) |
| GraphQL | SDL (Schema Definition Language) |
| gRPC | Protocol Buffers (.proto) |
| Eventos | AsyncAPI |
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.