Hexadevs
Volver al blog

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 header Location apuntando 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:

  1. Un code estable, no un mensaje. El frontend puede mostrar UI distinto según el código sin parsear strings.
  2. Un campo afectado (field) cuando aplica. Permite marcar el input en rojo.
  3. 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 /usuario y /users mezclados.
  • 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, no nombre_usuario ni userName, 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.