Representación de Recursos y Formatos de Datos
🎯 Objetivo
Regla
5.1: Se debe estructurar y formatear las representaciones de los recursos intercambiados de manera consistente y predecible, utilizando JSON como formato predominante.
5.2: Se debe utilizar la negociación de contenido (Accept, Content-Type) de forma estándar.
📖 Concepto y Definición
Regla
5.3: Los clientes deben interactuar con los recursos a través de sus representaciones, que son instantáneas del estado del recurso en un tipo de medio específico.
5.4: El formato de datos predeterminado y obligatorio para todas las APIs REST es JSON (application/json).
- Otros formatos (XML, etc.) solo deben considerarse bajo justificación explícita y aprobación, y su soporte no exime del soporte obligatorio de JSON. 5.5: La negociación de contenido debe realizarse mediante los encabezados HTTP estándar:
Accept(solicitud): El cliente debe usarlo para indicar los tipos de medio que puede entender. La API debe respetarlo.Content-Type(solicitud/respuesta): Debe usarse para indicar el tipo de medio del cuerpo del mensaje. En solicitudesPOST/PUT/PATCHcon cuerpo, el cliente debe enviarlo. En respuestas con cuerpo, el servidor debe enviarlo.
🤔 ¿Por qué es Importante? / Beneficios Clave (Contexto)
- Interoperabilidad: JSON es ampliamente soportado.
- Legibilidad y Facilidad de Uso: JSON es fácil de leer y parsear.
- Flexibilidad (Limitada por Norma): Aunque se norma JSON, la negociación de contenido permite teóricamente otros formatos.
- Desacoplamiento: El cliente se desacopla de la estructura interna del recurso.
✅ Buenas Prácticas y Recomendaciones Clave
Para JSON (Formato Obligatorio):
Regla
5.6: Los nombres de las claves (campos) en JSON deben ser descriptivos, en inglés, y seguir la convención camelCase (ej. firstName, totalAmount). Esta convención es obligatoria para toda la API.
5.7: Las fechas y horas deben representarse siempre en formato ISO 8601 string, preferiblemente en UTC (ej. 2023-10-27T10:30:00Z) o con offset (ej. 2023-10-27T12:30:00+02:00).
5.8: Los campos opcionales sin valor deben ser omitidos de la representación JSON. No se debe enviar null para campos opcionales ausentes, a menos que null tenga un significado semántico explícito y documentado para ese campo (ej. "explícitamente sin valor" vs. "valor no proporcionado/desconocido"). Esta distinción debe ser clara.
5.9: Los datos anidados deben estructurarse lógicamente usando objetos y arrays JSON.
5.10: Un campo debe tener siempre el mismo tipo de dato JSON (string, number, boolean, object, array) en todas las instancias de un recurso y entre versiones compatibles de la API.
5.11: Para campos que representan listas o colecciones, si la lista está vacía, se debe devolver un array JSON vacío []. No se debe usar null para representar una colección vacía, a menos que el campo en sí sea opcional y esté ausente (ver Norma 5.8).
5.12: Para campos string, una string vacía "" es un valor válido y distinto de null o un campo omitido. Su significado debe ser claro en el contexto del campo.
Negociación de Contenido:
Regla
5.13: La API debe respetar el encabezado Accept del cliente. Si el cliente solicita application/json (o */* y JSON es el predeterminado), la API debe responder con Content-Type: application/json.
5.14: Si el cliente no especifica Accept o especifica */*, la API debe devolver application/json por defecto.
5.15: Si el cliente solicita un formato explícito (distinto de */*) que la API no soporta (ej. application/xml si solo se soporta JSON), la API debe responder con 406 Not Acceptable.
5.16: El servidor debe incluir siempre el encabezado Content-Type: application/json en las respuestas que tienen cuerpo JSON.
5.17: Se requiere que los clientes envíen Content-Type: application/json en solicitudes POST/PUT/PATCH que contengan un cuerpo JSON. Si no se provee o es un tipo no soportado, la API debe responder con 415 Unsupported Media Type.
General (Estructura de Respuesta):
Regla
5.18: La API debe minimizar los datos transferidos (evitar "over-fetching"). Solo deben devolverse los datos necesarios para el contexto de la solicitud. Se deben considerar mecanismos de selección de campos (ver Sección 10 y 12). 5.19: La estructura general de la respuesta (éxito y error) debe ser consistente y predecible en toda la API, como se detalla a continuación y en la Sección 11 (Manejo de Errores).
Consistencia Integral en Estructuras de Respuesta (Éxito y Error):
Regla
5.20: Importancia Crítica: Una estructura de respuesta consistente es obligatoria para:
- Asegurar previsibilidad y facilidad de uso.
- Reducir la carga cognitiva de los desarrolladores.
- Simplificar el código cliente para procesar respuestas.
- Facilitar el mantenimiento y la evolución de la API.
- Mejorar el soporte de herramientas (documentación, SDKs, mocks).
5.21: Consistencia en Respuestas Exitosas:
- Envoltura de Datos (Data Wrapping):
- Para un único recurso, la respuesta debe estar envuelta usando la clave
"data":{ "data": { "id": 1, ... } }. - Para una lista de recursos, la respuesta debe estar envuelta. La clave principal debe ser
"data"y contendrá el array de recursos. - La envoltura permite añadir metadatos (paginación, enlaces HATEOAS) de forma limpia y consistente.
- Importante: La consistencia en el uso del envelope mínimo es obligatoria para todas las respuestas, facilitando el procesamiento por parte de los clientes y mejorando la mantenibilidad de la API.
- Para un único recurso, la respuesta debe estar envuelta usando la clave
- Nomenclatura de Campos: Se refuerza la Norma 5.6 (
camelCase). - Formatos de Datos Comunes: Se refuerza la Norma 5.7 (ISO 8601 para fechas) y debe asegurarse consistencia para otros tipos comunes (identificadores, booleanos, números).
- Estructura para Colecciones y Paginación: Si una respuesta devuelve una lista, la estructura para la paginación (si existe) debe ser idéntica en todos los endpoints que la soporten. Campos como
totalItems,totalPages,page,perPagedeben tener nombres y significados consistentes y estar documentados (ver Sección 10).// Ejemplo de paginación consistente obligatoria
{
"data": [ /* ... elementos ... */ ],
"_links": [
{
"rel": "self",
"href": "https://api.example.com/v1/resource?page=1&perPage=10",
"method": "GET"
},
{
"rel": "first",
"href": "https://api.example.com/v1/resource?page=1&perPage=10",
"method": "GET"
},
{
"rel": "next",
"href": "https://api.example.com/v1/resource?page=2&perPage=10",
"method": "GET"
}
],
"_meta": {
"timestamp": "2023-10-27T12:00:00Z",
"version": "1.0.0",
"pagination": {
"totalItems": 100,
"totalPages": 10,
"page": 1,
"perPage": 10
}
}
} - Enlaces HATEOAS: Si se utiliza HATEOAS (ver Sección 6), la forma en que se representan los enlaces (ej. dentro de un objeto
"_links"o"links"a nivel raíz o por recurso) y sus atributos (href,rel,method) debe ser uniforme en toda la API.
5.22: Consistencia en Respuestas de Error:
- La estructura del cuerpo de una respuesta de error JSON debe ser consistente en toda la API, independientemente del código de estado
4xxo5xx. Esto es obligatorio y se detalla en la Sección 11. - No se debe exponer información sensible (stack traces, etc.) en respuestas de error al cliente.
Estructura General del Diseño de Respuestas API
Regla
5.23: Todas las respuestas API deben seguir una estructura de envelope mínima + HATEOAS consistente, como se detalla a continuación.
Estructura de Respuestas de Éxito (Recurso Único)
Regla
5.24: Las respuestas de éxito para recursos únicos deben seguir esta estructura:
{
"data": {
// Datos del recurso solicitado
},
"_links": [
{
"rel": "self",
"href": "URL_completa_al_recurso",
"method": "GET"
},
{
"rel": "nombre_relacion",
"href": "URL_completa_a_accion_o_recurso_relacionado",
"method": "MÉTODO_HTTP"
}
],
"_meta": {
"timestamp": "ISO-8601-timestamp",
"version": "version_de_API"
}
}
Estructura de Respuestas de Éxito (Colecciones con Paginación)
Regla
5.25: Las respuestas de éxito para colecciones deben seguir esta estructura:
{
"data": [
// Array de recursos
],
"_links": [
{
"rel": "self",
"href": "URL_actual_con_parametros_paginacion",
"method": "GET"
},
{
"rel": "first",
"href": "URL_primera_pagina",
"method": "GET"
},
{
"rel": "prev",
"href": "URL_pagina_anterior",
"method": "GET"
},
{
"rel": "next",
"href": "URL_pagina_siguiente",
"method": "GET"
},
{
"rel": "last",
"href": "URL_ultima_pagina",
"method": "GET"
}
],
"_meta": {
"timestamp": "ISO-8601-timestamp",
"version": "version_de_API",
"pagination": {
"page": numero_pagina_actual,
"perPage": elementos_por_pagina,
"totalPages": total_paginas,
"totalItems": total_elementos
}
}
}
Estructura de Respuestas de Error
Regla
5.26: Las respuestas de error deben seguir esta estructura:
{
"error": {
"status": codigo_HTTP,
"error": "nombre_estandar_error",
"message": "descripcion_error_para_humanos",
"code": "CODIGO_ERROR_INTERNO",
"errors": [
{
"field": "campo_con_error",
"message": "descripcion_especifica_error"
}
]
},
"_meta": {
"timestamp": "ISO-8601-timestamp",
"version": "version_de_API"
},
"_links": [
{
"rel": "self",
"href": "URL_del_recurso",
"method": "MÉTODO_HTTP"
}
]
}
Componentes de la Estructura
Regla
5.27: Los componentes de la estructura deben cumplir con las siguientes especificaciones:
data: Contiene los datos del recurso o colección solicitada (solo para respuestas exitosas).error: Contiene la información detallada del error (solo para respuestas de error)._links: Array de enlaces HATEOAS que permiten la navegación y descubrimiento de recursos relacionados._meta: Metadatos de la respuesta, incluyendo timestamp y versión de la API.pagination: (Solo para colecciones) Información sobre la paginación actual, dentro de_meta.
Principios de Implementación
Regla
5.28: La implementación debe seguir estos principios:
- Minimalismo: Solo incluir lo necesario en la envelope.
- Autodescripción: Enlaces HATEOAS claros y descriptivos.
- Consistencia: Misma estructura en todas las respuestas.
- Separación: Clara distinción entre datos, enlaces y metadatos.
💡 Ejemplos Prácticos (Ilustrativos)
Ejemplo 1: Representación JSON de un Usuario (Norma 5.6, 5.7, 5.8, 5.11, 5.24)
// GET /users/123
// Content-Type: application/json (Norma 5.16)
{
"data": {
"userId": "123",
"username": "johndoe",
"firstName": "John",
"lastName": "Doe",
"email": "john.doe@example.com",
"isActive": true,
"dateJoined": "2022-01-15T09:00:00Z",
"lastLogin": "2023-10-26T15:45:10Z",
"middleName": null,
"preferences": {
"theme": "dark",
"notificationsEnabled": true
},
"roles": ["user", "editor"],
"tags": []
},
"_links": [
{
"rel": "self",
"href": "https://api.example.com/v1/users/123",
"method": "GET"
},
{
"rel": "update",
"href": "https://api.example.com/v1/users/123",
"method": "PUT"
},
{
"rel": "delete",
"href": "https://api.example.com/v1/users/123",
"method": "DELETE"
},
{
"rel": "orders",
"href": "https://api.example.com/v1/users/123/orders",
"method": "GET"
}
],
"_meta": {
"timestamp": "2023-10-27T12:00:00Z",
"version": "1.0.0"
}
}
Ejemplo 2: Lista de Recursos con Envoltura y Paginación (Norma 5.21, 5.25)
// GET /orders?status=pending&page=1&perPage=10
// Content-Type: application/json
{
"data": [
{
"orderId": "ord_001",
"status": "pending",
"totalAmount": 150.75,
"createdAt": "2023-10-27T11:00:00Z"
},
{
"orderId": "ord_002",
"status": "pending",
"totalAmount": 89.50,
"createdAt": "2023-10-27T11:05:00Z"
}
],
"_links": [
{
"rel": "self",
"href": "https://api.example.com/v1/orders?status=pending&page=1&perPage=10",
"method": "GET"
},
{
"rel": "first",
"href": "https://api.example.com/v1/orders?status=pending&page=1&perPage=10",
"method": "GET"
},
{
"rel": "prev",
"href": "https://api.example.com/v1/orders?status=pending&page=1&perPage=10",
"method": "GET"
},
{
"rel": "next",
"href": "https://api.example.com/v1/orders?status=pending&page=2&perPage=10",
"method": "GET"
},
{
"rel": "last",
"href": "https://api.example.com/v1/orders?status=pending&page=5&perPage=10",
"method": "GET"
}
],
"_meta": {
"timestamp": "2023-10-27T12:00:00Z",
"version": "1.0.0",
"pagination": {
"page": 1,
"perPage": 10,
"totalPages": 5,
"totalItems": 42
}
}
}
Ejemplo 3: Respuesta de Error (Norma 5.26)
// POST /users
// Content-Type: application/json
// Status: 400 Bad Request
{
"error": {
"status": 400,
"error": "ValidationError",
"message": "Los datos proporcionados no son válidos",
"code": "INVALID_USER_DATA",
"errors": [
{
"field": "email",
"message": "El formato del correo electrónico no es válido"
},
{
"field": "password",
"message": "La contraseña debe tener al menos 8 caracteres"
}
]
},
"_meta": {
"timestamp": "2023-10-27T12:00:00Z",
"version": "1.0.0"
},
"_links": [
{
"rel": "self",
"href": "/v1/users",
"method": "POST"
}
]
}
🛠️ Herramientas y Consideraciones Adicionales (Contexto)
- JSON Schema: Se recomienda su uso para definir y validar la estructura de las representaciones JSON (solicitudes y respuestas).
- Tipos de Medios Personalizados: Para versionado o APIs muy específicas, se pueden definir tipos de medios propios (ej.
application/vnd.example.user.v1+json), pero deben basarse en JSON. - Datos Binarios: Para transferir archivos, se deben usar tipos como
image/jpeg,application/pdf, oapplication/octet-stream. La transferencia puede ser directa o usandomultipart/form-data. Esto debe estar claramente documentado. - Compresión: Se debe usar compresión HTTP (Gzip, Brotli) para reducir el tamaño de las representaciones y mejorar el rendimiento. El cliente indica soporte con
Accept-Encodingy el servidor responde conContent-Encoding.