Definición
Un response schema es una especificación formal que define qué datos retorna un endpoint de API a los clientes. Describe la estructura de cuerpos de respuesta para diferentes códigos de estado HTTP (200, 201, 400, 404, 500), incluyendo tipos de datos, campos obligatorios, estructuras anidadas y posibles variaciones. A diferencia de los request schemas que validan datos entrantes, los response schemas sirven como un contrato que garantiza a los clientes recibir datos en un formato predecible.
Los response schemas son cruciales para consumidores de API que generan clientes type-safe. Cuando un SDK de TypeScript o Java se auto-genera desde una spec OpenAPI, los response schemas se convierten en garantías en tiempo de compilación - los desarrolladores obtienen autocompletado, verificación de tipos y documentación directamente en su IDE. Los cambios en la estructura de respuesta son inmediatamente visibles como cambios breaking.
Mientras la validación de peticiones previene que datos incorrectos entren a tu sistema, la validación de respuestas detecta bugs de backend. Si tu código accidentalmente retorna un string donde el schema promete un número, la validación lo detecta antes de que la respuesta llegue a los clientes. Esto es especialmente valioso en arquitecturas de microservicios donde los servicios evolucionan independientemente.
Ejemplo
API de Perfil de Usuario: Un endpoint GET /users/{id} define response schemas para múltiples códigos de estado: 200 retorna un objeto User (id, email, name, createdAt), 404 retorna un objeto Error (code, message), 500 retorna un ServerError (requestId, message). Los SDKs de clientes usan estos schemas para deserializar JSON en objetos fuertemente tipados.
Resultados de Búsqueda: Una API de búsqueda de productos define un response schema con metadata de paginación: results (array de objetos Product), totalCount (integer), page (integer), pageSize (integer), nextPageToken (string o null). El schema garantiza que los clientes siempre pueden acceder de forma segura a estos campos sin verificaciones null en el objeto contenedor.
Respuestas Polimórficas: Una API de notificaciones retorna diferentes schemas basados en el tipo de notificación usando un discriminador. Todas las notificaciones comparten campos comunes (id, timestamp, userId), pero las notificaciones email incluyen emailAddress y subject, mientras las notificaciones push incluyen deviceId y payload. El schema usa oneOf para modelar esto.
Actualizaciones Parciales: Un endpoint PATCH /users/{id} retorna el objeto User completo en la respuesta 200, aunque solo se actualizaron campos específicos. El response schema asegura que los clientes siempre reciben el estado completo actual, no solo los campos cambiados.
Respuestas de Error: Una API define un schema Error consistente para todas las respuestas 4xx y 5xx con campos obligatorios (code, message, timestamp) y campos opcionales (array details, URL de documentación). Esto asegura que todas las respuestas de error en cada endpoint sigan el mismo formato.
Analogía
La Etiqueta del Producto: Así como los productos alimenticios tienen etiquetas nutricionales obligatorias con un formato estándar (calorías, grasa, carbohidratos, proteína), los response schemas definen el formato estándar para datos de API. Los consumidores confían en esta consistencia - no necesitan verificar si las calorías están en gramos u onzas porque el formato de etiqueta lo garantiza.
La Plantilla de Recibo: Cuando compras algo, el recibo siempre tiene la misma estructura: artículos comprados, precios, subtotal, impuesto, total, método de pago. Puedes escribir software de seguimiento de gastos sabiendo que los recibos siguen este formato. Los response schemas son como plantillas de recibos - garantizan estructura incluso cuando los datos específicos cambian.
El Formato del Reporte del Clima: Los servicios meteorológicos siempre reportan temperatura, humedad, velocidad del viento, pronóstico en formatos predecibles. Los desarrolladores que construyen apps del clima confían en esta estructura. Si un servicio de repente retornara temperatura en Kelvin en lugar de Celsius sin actualizar el schema, las apps se romperían. Los response schemas previenen estas sorpresas.
Ejemplo de Código
# Definición de Response [Schema](https://reference.apios.info/es/terms/schema/) en [OpenAPI 3](https://reference.apios.info/es/terms/openapi-3/).x
paths:
/api/users/{userId}:
get:
summary: Get user by ID
parameters:
- name: userId
in: path
required: true
schema:
type: string
format: uuid
responses:
'200':
description: User found
content:
application/json:
schema:
$ref: '#/components/schemas/User'
example:
id: "123e4567-e89b-12d3-a456-426614174000"
email: "[email protected]"
username: "alice_smith"
role: "user"
createdAt: "2024-01-15T10:30:00Z"
profile:
firstName: "Alice"
lastName: "Smith"
bio: "Software engineer"
'404':
description: User not found
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
example:
code: "USER_NOT_FOUND"
message: "No user exists with the provided ID"
timestamp: "2024-01-20T14:23:00Z"
'500':
description: Internal server error
content:
application/json:
schema:
$ref: '#/components/schemas/ServerError'
components:
schemas:
User:
type: object
required:
- id
- email
- username
- role
- createdAt
properties:
id:
type: string
format: uuid
email:
type: string
format: email
username:
type: string
role:
type: string
enum: [admin, moderator, user]
createdAt:
type: string
format: date-time
profile:
type: object
properties:
firstName:
type: string
lastName:
type: string
bio:
type: string
maxLength: 500
Error:
type: object
required:
- code
- message
- timestamp
properties:
code:
type: string
description: Machine-readable error code
message:
type: string
description: Human-readable error message
timestamp:
type: string
format: date-time
details:
type: array
items:
type: string
ServerError:
allOf:
- $ref: '#/components/schemas/Error'
- type: object
required:
- requestId
properties:
requestId:
type: string
format: uuid
description: Unique ID for debugging this request
Validando respuestas en tests (TypeScript):
import Ajv from "ajv";
import addFormats from "ajv-formats";
const ajv = new Ajv();
addFormats(ajv);
const userSchema = {
type: "object",
required: ["id", "email", "username", "role", "createdAt"],
properties: {
id: { type: "string", format: "uuid" },
email: { type: "string", format: "email" },
username: { type: "string" },
role: { type: "string", enum: ["admin", "moderator", "user"] },
createdAt: { type: "string", format: "date-time" },
profile: {
type: "object",
properties: {
firstName: { type: "string" },
lastName: { type: "string" },
bio: { type: "string", maxLength: 500 }
}
}
}
};
const validate = ajv.compile(userSchema);
// Contract testing - validar respuesta real de API
async function testGetUser() {
const response = await fetch('https://api.example.com/users/123');
const data = await response.json();
const valid = validate(data);
if (!valid) {
console.error("¡Falló validación de schema de respuesta!");
console.error(validate.errors);
throw new Error("La respuesta de API no coincide con el schema");
}
console.log("✓ La respuesta coincide con el schema");
return data;
}
// Uso en tests de integración
describe('GET /users/:id', () => {
it('retorna usuario que coincide con response schema', async () => {
const user = await testGetUser();
expect(validate(user)).toBe(true);
});
it('maneja 404 con schema de error', async () => {
const response = await fetch('https://api.example.com/users/nonexistent');
expect(response.status).toBe(404);
const error = await response.json();
const errorSchema = {
type: "object",
required: ["code", "message", "timestamp"],
properties: {
code: { type: "string" },
message: { type: "string" },
timestamp: { type: "string", format: "date-time" }
}
};
const validateError = ajv.compile(errorSchema);
expect(validateError(error)).toBe(true);
});
});
Diagrama
sequenceDiagram
participant Client
participant API
participant Handler
participant Validator
participant Monitor
Client->>API: GET /users/123
API->>Handler: Enrutar petición
Handler->>Handler: Obtener datos de usuario
Handler->>Validator: Validar respuesta contra schema
alt Schema Válido
Validator-->>Handler: Pasar
Handler->>API: Retornar respuesta
API-->>Client: 200 OK + objeto User
else Schema Inválido
Validator-->>Handler: Violación de schema
Handler->>Monitor: Alerta - Discrepancia response schema
Handler->>API: Retornar error genérico
API-->>Client: 500 Internal Server Error
end
Note over Monitor: Registra violaciones de schema
para depuración
Notas de Seguridad
CRÍTICO - …
Configuración y Validación:
- Nunca expongas detalles de error internos en response schemas para producción.
- Define schemas separados para respuestas de error de desarrollo y producción.
- Valida respuestas en tests para prevenir fugas accidentales de datos.
Monitoreo y Protección:
- Ten cuidado con additionalProperties true en response schemas - podría filtrar campos extra.
- Usa validación de respuestas para detectar bugs que retornan datos sensibles no definidos en el schema.
- Asegura que los schemas de error no incluyan stack traces o mensajes de error de base de datos.
- Limita la tasa basada en fallos de validación de respuestas para detectar intentos de sondeo.
Mejores Prácticas
- Definir respuestas para todos los códigos de estado - Incluye 200, 201, 400, 401, 404, 500 como mínimo
- Usar schema de error consistente - Todos los endpoints deben retornar errores en el mismo formato
- Proporcionar ejemplos realistas - Muestra datos de respuesta reales, no schemas abstractos
- Validar en tests - Los tests de contrato deben validar respuestas contra schemas
- Versionar response schemas - Rastrea cambios breaking cuando la estructura de respuesta evoluciona
- Documentar required vs opcional - Sé explícito sobre qué campos de respuesta están garantizados
- Usar discriminadores para variantes - Modela respuestas polimórficas con oneOf y discriminadores
- Incluir metadata de paginación - Para endpoints de lista, define totalCount, page, pageSize en schema
Errores Comunes
Faltan schemas de error: Solo definir respuestas de éxito (200, 201) y olvidar documentar estructuras de respuesta de error. Los clientes necesitan conocer formatos de error también.
Respuestas de error inconsistentes: Diferentes endpoints retornan errores en diferentes formatos. Esto rompe la lógica de manejo de errores del cliente.
Sobre-prometer con campos obligatorios: Marcar campos como obligatorios en el schema pero a veces no retornarlos. Esto rompe clientes type-safe.
No validar respuestas: Solo validar peticiones mientras se dejan bugs de backend producir respuestas inválidas. La validación de respuestas detecta problemas de backend temprano.
Exponer demasiado en dev: Usar el mismo response schema para desarrollo y producción, filtrando accidentalmente stack traces o información de debug a clientes de producción.
Ignorar additionalProperties: No especificar si pueden aparecer campos extra. Los clientes no saben si deben ignorar campos desconocidos o tratarlos como errores.
Sin schema para errores 500: Asumir que los errores internos son impredecibles. Incluso las respuestas 500 deben seguir un schema para logging y alertas consistentes.