Patrones de Diseño de JSON API: Construyendo Mejores REST APIs
Una API bien diseñada es un placer de usar. Una mal diseñada crea errores, frustración y deuda técnica. Esta guía cubre patrones probados en batalla para diseñar JSON REST APIs que sean consistentes, descubribles y mantenibles.
Nomenclatura de Recursos
Los recursos son sustantivos, no verbos. Usa sustantivos en plural para colecciones y recursos individuales mediante ID:
GET /api/users # List users
POST /api/users # Create a user
GET /api/users/123 # Get user 123
PUT /api/users/123 # Update user 123
DELETE /api/users/123 # Delete user 123
Convenciones de nomenclatura:
- Usa minúsculas con guiones:
/api/blog-posts(noblogPostsniblog_posts) - Anida recursos relacionados:
/api/users/123/orders - Limita la anidación a 2 niveles:
/api/users/123/orders/456(no más profundo) - Evita verbos en URLs:
/api/users/123/activatees aceptable para acciones que no se mapean a CRUD
Envoltorio de Respuesta
Envuelve las respuestas en un envoltorio consistente:
{
"data": {
"id": "123",
"type": "user",
"attributes": {
"name": "Alice",
"email": "alice@example.com",
"createdAt": "2024-01-15T10:30:00Z"
}
},
"meta": {
"requestId": "req_abc123"
}
}
Para colecciones:
{
"data": [
{ "id": "123", "name": "Alice" },
{ "id": "456", "name": "Bob" }
],
"meta": {
"total": 142,
"page": 1,
"perPage": 20
}
}
Valida las respuestas de tu API con nuestro Validador JSON para asegurar que coincidan con tu esquema.
Paginación
Tres enfoques comunes:
Basada en Offset (La Más Simple)
GET /api/users?page=2&per_page=20
{
"data": [...],
"meta": {
"page": 2,
"perPage": 20,
"total": 142,
"totalPages": 8
}
}
Ventaja: Simple, permite saltar a cualquier página. Desventaja: Resultados inconsistentes con inserciones/eliminaciones concurrentes.
Basada en Cursor (La Más Confiable)
GET /api/users?cursor=eyJpZCI6MTIzfQ&limit=20
{
"data": [...],
"meta": {
"hasNext": true,
"nextCursor": "eyJpZCI6MTQzfQ"
}
}
Ventaja: Consistente con cambios concurrentes, eficiente en conjuntos de datos grandes. Desventaja: No se puede saltar a páginas arbitrarias.
Basada en Keyset (La Más Eficiente)
GET /api/users?after_id=123&limit=20
Usa el ID del último elemento (u otro campo ordenado) para obtener la siguiente página. Similar a la basada en cursor pero con parámetros transparentes.
Recomendación: Usa basada en cursor para feeds en tiempo real y conjuntos de datos grandes. Usa basada en offset para paneles de administración donde importa saltar entre páginas.
Filtrado y Ordenamiento
Filtrado
GET /api/users?status=active&role=admin
GET /api/users?created_after=2024-01-01
GET /api/users?search=alice
Para filtros complejos, considera un parámetro de consulta dedicado:
GET /api/users?filter[status]=active&filter[role]=admin
Ordenamiento
GET /api/users?sort=name # Ascendente
GET /api/users?sort=-created_at # Descendente (prefijo con -)
GET /api/users?sort=-created_at,name # Múltiples campos
Manejo de Errores
Las respuestas de error consistentes son críticas para la usabilidad de la API:
{
"error": {
"code": "VALIDATION_ERROR",
"message": "Request validation failed",
"details": [
{
"field": "email",
"message": "Must be a valid email address",
"value": "not-an-email"
},
{
"field": "age",
"message": "Must be between 0 and 150",
"value": -5
}
]
},
"meta": {
"requestId": "req_abc123"
}
}
Códigos de estado HTTP a usar:
| Código | Significado | Cuándo |
|---|---|---|
| 200 | OK | GET, PUT exitoso |
| 201 | Creado | POST exitoso |
| 204 | Sin Contenido | DELETE exitoso |
| 400 | Solicitud Incorrecta | Errores de validación |
| 401 | No Autorizado | Autenticación faltante/inválida |
| 403 | Prohibido | Autenticación válida, permisos insuficientes |
| 404 | No Encontrado | El recurso no existe |
| 409 | Conflicto | Recurso duplicado, conflicto de versión |
| 422 | No Procesable | Semánticamente inválido |
| 429 | Demasiadas Solicitudes | Límite de tasa excedido |
| 500 | Error Interno | Error inesperado del servidor |
Versionado
Tres enfoques:
Ruta en URL (Recomendado)
GET /api/v1/users
GET /api/v2/users
Ventaja: Explícito, fácil de enrutar, fácil de probar.
Basado en Header
GET /api/users
Accept: application/vnd.myapi.v2+json
Ventaja: URLs limpias. Desventaja: Más difícil de probar, menos descubrible.
Parámetro de Consulta
GET /api/users?version=2
Ventaja: Fácil de probar. Desventaja: Semántica de parámetro opcional.
Recomendación: El versionado por ruta en URL es la opción más práctica. Es explícito, cacheable y funciona con todas las herramientas HTTP.
Manejo de Fechas y Horas
Siempre usa ISO 8601 en UTC:
{
"createdAt": "2024-01-15T10:30:00Z",
"updatedAt": "2024-01-15T14:22:33Z",
"expiresAt": "2024-12-31T23:59:59Z"
}
Nunca uses timestamps Unix en respuestas de API — son ambiguos (segundos vs milisegundos) y no son legibles por humanos. Para más sobre manejo de timestamps, consulta nuestra guía de Timestamps Unix.
Limitación de Tasa
Comunica los límites de tasa en los headers de respuesta:
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 67
X-RateLimit-Reset: 1705312800
Retry-After: 30
Devuelve 429 Too Many Requests cuando se exceda el límite, con un mensaje de error claro y el header Retry-After.
HATEOAS (Opcional pero Poderoso)
Incluye enlaces a recursos y acciones relacionadas:
{
"data": {
"id": "123",
"name": "Alice",
"links": {
"self": "/api/users/123",
"orders": "/api/users/123/orders",
"avatar": "/api/users/123/avatar"
}
}
}
Esto hace que tu API sea auto-documentada y reduce la construcción de URLs del lado del cliente.
FAQ
¿Debería usar la especificación JSON:API o diseñar mi propio formato?
La especificación JSON:API (jsonapi.org) proporciona un estándar integral, pero puede ser verboso para APIs simples. Para la mayoría de los proyectos, diseñar un formato personalizado más simple siguiendo los patrones de esta guía es más práctico. Usa JSON:API si necesitas bibliotecas de cliente automáticas y un estándar estricto.
¿Cómo debo manejar actualizaciones parciales (PATCH vs PUT)?
Usa PUT para reemplazo completo de recursos (el cliente envía todos los campos). Usa PATCH para actualizaciones parciales (el cliente envía solo los campos modificados). PATCH con JSON Merge Patch (RFC 7396) es el enfoque más simple: envía un objeto JSON con solo los campos a actualizar, y null para eliminar un campo.
Recursos Relacionados
- Formateador JSON — Formatea respuestas de API para legibilidad
- Guía de Validación de JSON Schema — Valida payloads de API con JSON Schema
- JWT Tokens Explicados — Asegura tus APIs con JSON Web Tokens