Diseño de APIs REST: recursos, verbos y los errores que casi nadie arregla
Por Equipo Hexadevs · 12 sept 2026 · 3 min de lectura
Una buena API REST es predecible: si aprendés las reglas de una, podés usar cualquiera sin leer documentación. Una mala API REST te obliga a abrir Postman para hacer un GET. La diferencia entre las dos está en unas pocas decisiones de diseño.
Recursos, no acciones
Las URLs describen cosas, no operaciones:
# Bien
GET /usuarios
GET /usuarios/42
POST /usuarios
PATCH /usuarios/42
DELETE /usuarios/42
# Mal: verbos en la URL, sustantivos singulares inconsistentes
GET /obtenerUsuario/42
POST /crearUsuario
POST /eliminarUsuario
Los verbos van en el método HTTP. La URL siempre es un sustantivo (en plural). Esta sola regla ya te resuelve la mitad de los problemas de diseño.
Status codes que sí importan
No devuelvas siempre 200 con un JSON adentro. Los status codes son parte de tu API:
200 OK— todo salió bien, hay cuerpo.201 Created— creaste algo. Devolvé el recurso nuevo en el body y el headerLocationapuntando a él.204 No Content— todo salió bien, no hay cuerpo (típico en DELETE).400 Bad Request— el cliente mandó algo mal formado (JSON inválido).401 Unauthorized— sin autenticación o autenticación inválida.403 Forbidden— autenticado pero sin permiso.404 Not Found— el recurso no existe.409 Conflict— duplicado, conflicto de versión, validación de negocio.422 Unprocessable Entity— JSON válido pero semánticamente incorrecto.500 Internal Server Error— algo se rompió en el servidor.
401 y 403 no son intercambiables. 400 y 422 tampoco: el primero es “no entiendo lo que mandaste”, el segundo es “entendí pero no lo puedo procesar”.
Errores que el frontend puede usar
{
"error": {
"code": "email_already_registered",
"message": "El email ya está registrado",
"field": "email",
"details": {
"suggestion": "Probá con otro email o iniciá sesión"
}
}
}
Tres cosas que un buen error tiene:
- Un
codeestable, no un mensaje. El frontend puede mostrar UI distinto según el código sin parsear strings. - Un campo afectado (
field) cuando aplica. Permite marcar el input en rojo. - Mensaje legible para humanos, pero sin información sensible ni detalles internos.
Versionado
GET /api/v1/usuarios
GET /api/v2/usuarios
Cuando cambies algo que rompe la compatibilidad (renombrar un campo, eliminar un endpoint), subí la versión. No uses versionado por header (Accept: application/vnd.api.v2+json) a menos que tengas una razón fuerte: el versionado en URL es más fácil de debuggear y cachear.
Paginación, filtros y orden
No devuelvas todo el dataset sin límite: un endpoint sin paginación es un bug de rendimiento esperando a ocurrir. Algunos patrones estándar:
GET /usuarios?page=2&per_page=20
GET /usuarios?cursor=eyJpZCI6MTAwfQ&limit=20 # cursor-based, mejor para feeds
GET /usuarios?rol=admin&activo=true
GET /usuarios?sort=created_at&order=desc
Cursor-based es preferible para listas grandes o feeds: escala mejor que offset y no se rompe cuando se insertan items nuevos.
Convenciones que evitan discusiones
- Plurales consistentes:
/usuarios, no/usuarioy/usersmezclados. - Sin verbos en URL: si necesitás un verbo (
/usuarios/42/activar), probablemente falta un sub-recurso o un endpoint de acción. - Campos en JSON consistentes:
nombre, nonombre_usuarioniuserName, salvo que tu equipo ya convenga otra cosa. - Fechas en ISO 8601:
"2026-09-12T18:30:00Z", nunca timestamps Unix sin zona horaria.
Una API se juzga por lo bien que se comporta cuando algo falla, no cuando todo sale perfecto.
Una API que sigue estas convenciones reduce la cantidad de documentación que tenés que escribir. Y la documentación que escribas, el frontend la va a leer menos porque la API ya se comporta como esperan.