Visión General
REST es el estilo arquitectónico dominante para diseñar APIs de red. Una API REST bien diseñada usa la semántica HTTP de manera consistente, provee URLs predecibles y devuelve códigos de estado significativos. Un diseño deficiente conduce a consumidores confundidos, clientes rotos e integraciones frágiles.
Cuándo Usar
Usa este recurso cuando:
- Diseñes una API pública o interna desde cero
- Refactorices una API estilo RPC legacy a REST
- Documentes una API con OpenAPI/Swagger
- Elijas entre REST, GraphQL o gRPC para un nuevo servicio
Cuándo Evitar
- Comunicación bidireccional en tiempo real: REST es solo request-response.
- Queries complejas controladas por el cliente: GraphQL permite a los clientes pedir exactamente los campos que necesitan. REST hace over-fetch o under-fetch.
- Llamadas internas de alto rendimiento: gRPC con Protobuf es 5-10x más rápido que REST/JSON para microservicios internos.
- Streaming de payloads grandes: REST bufferiza respuestas completas.
Solución
Nomenclatura de Recursos
GET /users # Listar usuarios
GET /users/:id # Obtener un usuario
POST /users # Crear un usuario
PUT /users/:id # Actualización completa
PATCH /users/:id # Actualización parcial
DELETE /users/:id # Eliminar un usuario
GET /users/:id/orders # Recurso anidado
Códigos de Estado
// Respuestas exitosas
200 OK // GET, PUT, DELETE exitoso
201 Created // POST exitoso
204 No Content // DELETE exitoso (opcional)
// Errores del cliente
400 Bad Request // Fallo de validación
401 Unauthorized // Token de auth faltante
403 Forbidden // Permisos insuficientes
404 Not Found // El recurso no existe
409 Conflict // Duplicado o conflicto de estado
422 Unprocessable // Error de validación semántica
// Errores del servidor
500 Internal Error // Fallo inesperado del servidor
502 Bad Gateway // Fallo upstream
503 Service Unavail // Rate limiting o mantenimiento
Paginación con Cursor
{
"data": [...],
"pagination": {
"next_cursor": "eyJpZCI6MTAwfQ==",
"prev_cursor": null,
"has_more": true
}
}
Formato de Respuesta de Error
Devuelve errores en una estructura consistente:
{
"error": {
"code": "VALIDATION_ERROR",
"message": "Email is required",
"field": "email",
"details": [{"field": "email", "message": "Email is required"}]
}
}
Estrategias de Versionado
// Basado en URL (más común)
GET /v1/users
GET /v2/users
// Basado en header (URLs más limpias, más difícil de testear)
Accept: application/vnd.api+json;version=1
// Query parameter (fácil pero no recomendado)
GET /users?version=1
El versionado basado en URL es el más explícito y fácil de testear. El basado en header es más limpio pero más difícil de debuggear en navegadores.
Claves de Idempotencia
Para requests POST que pueden ser reintentados (pagos, creación de órdenes), acepta una clave de idempotencia:
POST /payments
Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000
Content-Type: application/json
{"amount": 1000, "currency": "USD"}
El servidor almacena la clave y devuelve la respuesta original en reintentos. Consulta Endpoints Idempotentes para implementación.
Respuesta de Rate Limiting
HTTP/1.1 429 Too Many Requests
Content-Type: application/json
Retry-After: 60
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1719900000
{
"error": {
"code": "RATE_LIMITED",
"message": "Too many requests. Retry after 60 seconds."
}
}
Explicación
REST aprovecha HTTP como protocolo de aplicación, no solo como transporte:
- Idempotencia: GET, PUT, DELETE deben ser seguros de reintentar. Consulta Endpoints Idempotentes para patrones. POST no es idempotente.
- Sin estado: Cada request contiene toda la información necesaria; sin sesión del lado del servidor.
- Cacheabilidad: Consulta Manejo de CORS para configuración de headers.
- HATEOAS: Incluye links a recursos relacionados (opcional pero mejora descubribilidad).
Variantes
| Estilo | Caso de Uso | Notas |
|---|---|---|
| REST | CRUD, orientado a recursos | Ecosistema maduro; caching HTTP |
| GraphQL | Queries flexibles; mobile | Un solo endpoint; client-driven |
| gRPC | Microservicios internos | Binario; streaming; schema-first |
| JSON-RPC | RPC simple | Liviano; menos nativo HTTP |
| tRPC | TypeScript end-to-end | Type-safe; sin codegen; solo TS |
| SOAP | Enterprise; banca | XML; WS-Security; verboso |
Avanzado: Negociación de Contenido
Soporta múltiples formatos de respuesta vía headers Accept:
GET /users/42
Accept: application/json # default
Accept: application/xml # clientes legacy
Accept: application/csv # exportación de datos
El servidor selecciona el serializer basado en Accept. Devuelve 406 Not Acceptable si el formato no está soportado.
Avanzado: Requests Condicionales
Usa ETag e If-None-Match para caching:
# Primera request
GET /users/42
ETag: "abc123"
# Request subsiguiente
GET /users/42
If-None-Match: "abc123"
# El servidor devuelve 304 si no cambió
HTTP/1.1 304 Not Modified
Para updates concurrentes, usa If-Match con ETag para optimistic locking:
PUT /users/42
If-Match: "abc123"
Si el ETag ya no coincide (alguien más modificó el recurso), devuelve 412 Precondition Failed.
Lo que funciona
- Usa sustantivos plurales: /orders, no /order ni /getOrder
- Versiona en la URL: /v1/users (más explícito que headers)
- Devuelve estructura consistente: { data, error, meta }
- Soporta filtrado: GET /users?
- Rate limit desde el inicio: Devuelve 429 con header Retry-After. Consulta Rate Limiting con Redis para implementación.
Errores Comunes
- Usar verbos en URLs: /createUser, /getOrders — usa sustantivos y métodos HTTP
- Ignorar códigos HTTP: Devolver 200 con cuerpo de error rompe middleware. Consulta Manejo de Errores para uso de códigos de estado.
- No versionar: Cambios breaking sin versionado abandonan clientes existentes
- Over-fetching: Devolver objetos anidados enormes cuando el cliente necesita un subset
- Faltar negociación de contenido: No respetar Accept y Content-Type headers
Lectura Adicional
- Documentación oficial: consulta la referencia actualizada del framework o herramienta utilizada.
- Guías relacionadas: explora las guías de rest-api y http para profundizar.
- Patrones complementarios: revisa los patrones de diseño aplicables a tu stack tecnológico.
- Postmortems públicos: estudia incidentes reales de equipos que enfrentaron problemas similares en producción.
Notas de Producción
- Despliega gradualmente usando canary o blue-green para detectar regresiones temprano.
- Configura alertas para errores, latencia p99 y tasa de fallos antes de habilitar en producción.
- Documenta el rollback en el runbook; prueba el procedimiento en staging al menos una vez por trimestre.
- Revisa logs estructurados con correlation IDs para trazar requests end-to-end en incidentes.
Puntos Clave
- Aplica diseño de apis rest: lo que funciona cuando necesites una solución práctica para tu caso de uso.
- Monitorea el rendimiento después de implementar; mide latencia, errores y uso de recursos antes y después.
- Revisa la sección de Troubleshooting ante errores comunes; la mayoría tienen causa raíz documentada con solución.
- Mantén dependencias actualizadas y ejecuta tests en CI para prevenir regresiones en producción.
Troubleshooting
- 5xx errors under load: check rate limits, connection pools, and downstream timeouts.
- CORS errors in the browser: confirm allowed origins, methods, and headers. Preflight requests must return the right headers before the actual request.
- Unexpected 404s: verify route definitions, path parameters, and base paths. Watch for trailing slashes and URL encoding differences.
- Authentication failures: validate token expiry, signature algorithms, and clock skew. Log rejected tokens without exposing secrets.
- Slow response times: profile the slowest percentiles.
Errores Comunes en Producción
- Copiar el ejemplo sin adaptarlo a volúmenes y modos de fallo reales.
- Saltar tests de carga e inyección de errores antes del primer despliegue productivo.
- Codificar valores fijos que deberían ser configurables por entorno.
- Olvidar agregar logging y monitoreo en cada paso.
- Desplegar sin plan de rollback ni estrategia de backup probada.
- Asumir que el ejemplo mínimo escalará sin agregar caché o procesamiento por lotes.
- No documentar la versión y configuración usadas en producción.
- Dejar la receta sin cambios cuando evolucionan las dependencias o la escala.
Preguntas frecuentes
¿Debería usar PUT o PATCH para actualizaciones?
PUT para reemplazo completo (todos los campos requeridos). PATCH para actualizaciones parciales (solo campos cambiados). PUT es idempotente: enviar el mismo PUT dos veces produce el mismo estado. PATCH puede ser idempotente pero no se requiere que lo sea.
¿Cómo manejo uploads de archivos en REST?
Usa multipart/form-data para uploads simples. Para archivos grandes, usa signed URLs (S3, GCS) o uploads resumibles. El cliente sube directamente al object storage, luego notifica a tu API con la ubicación del archivo. Esto evita streamear archivos grandes a través de tu servidor de API.
¿Vale la pena implementar HATEOAS?
Para APIs públicas consumidas por diversos clientes, sí — mejora la descubribilidad y reduce el harcoding de URLs. Para APIs internas con clientes generados, opcional. La mayoría de las APIs en producción omiten HATEOAS y documentan las URLs en specs de OpenAPI.
¿Cómo manejo la paginación para datasets grandes?
Usa paginación basada en cursor para datasets grandes o que cambian frecuentemente. La paginación basada en offset (page=2&limit=20) es más simple pero salta items cuando se insertan datos entre requests. Codifica el cursor como base64 de la sort key del último item.
¿Qué métodos HTTP debería usar?
GET (lectura, cacheable), POST (creación, no idempotente), PUT (update completo, idempotente), PATCH (update parcial), DELETE (eliminación, idempotente). Nunca uses GET para cambios de estado — rompe el caching y viola la semántica HTTP.
¿Cómo versiono mi API?
El versionado basado en URL (/v1/users) es el más común y fácil de testear. Incrementa la versión en cambios breaking: campos eliminados, tipos cambiados, semántica modificada. Cambios no-breaking (nuevos campos, nuevos endpoints) no requieren incrementar la versión.
¿Debería envolver las respuestas en un envelope?
Para endpoints de listado, sí — incluye metadata de paginación. Para recursos individuales, el envelope es opcional. Si envuelves, usa una estructura consistente: { data, error, meta }. Algunas APIs devuelven data directamente con info de error en headers.
¿Cómo manejo la autenticación en REST?
Bearer tokens en el header Authorization: Authorization: Bearer <token>. API keys en headers (X-API-Key) para casos simples. Evita poner tokens en parámetros de URL — aparecen en logs del servidor e historial del navegador.
¿Cuál es la diferencia entre 401 y 403?
401 Unauthorized significa que la request carece de credenciales de autenticación. 403 Forbidden significa que las credenciales son válidas pero el usuario carece de permisos para el recurso específico. Siempre devuelve 401 antes de auth, 403 después de auth pero sin permisos.
¿Cómo manejo operaciones de larga duración?
Devuelve 202 Accepted con una URL de estado. El cliente hace polling de la URL de estado hasta que la operación completa. Para webhooks, devuelve 202 y envía un POST a la URL del webhook del cliente cuando termine. Consulta Async API Pattern para patrones.
Recursos Relacionados
Plantilla de Respuesta de Error de API
Una plantilla reutilizable para respuestas de error de API consistentes, informativas y amigables para desarrolladores que reducen el tiempo de depuración.
GuideGuía de Diseño de APIs REST
Una Referencia Detallada para diseñar APIs REST limpias, escalables y mantenibles.
RecipeLlamar a una API REST: Python, JS, Java y Go
Cómo hacer peticiones HTTP a una API REST y manejar la respuesta JSON en Python, JavaScript, Java y Go.
RecipeManejo Correcto de CORS
Cómo configurar headers de Cross-Origin Resource Sharing (CORS) correctamente para APIs, SPAs y funciones serverless sin abrir agujeros de seguridad.
RecipeEndpoints de API Idempotentes
Cómo diseñar e implementar endpoints de API idempotentes que manejen retries, requests duplicados y fallas de red sin efectos secundarios.
RecipeAPI gRPC con Protocol Buffers
Implementa una API gRPC con Protocol Buffers. Cubre definición de servicios, generación de código y ejemplos cliente/servidor en Python, Java y Go.