diseño de respuestas de error API · códigos de error consistentes API · depuración de errores API · mejores prácticas de manejo de errores API

Diseño de Respuestas de Error Consistentes en APIs: Guía Práctica para Desarrolladores Independientes

Aprende a diseñar respuestas de error consistentes y accionables que ayuden a los desarrolladores a depurar rápidamente, sin filtrar información sensible ni abrumarlos con jerga técnica.

Publicado:

Por Qué las Respuestas de Error Importan Más de lo que Piensas

Cuando tu API falla —y fallará—, tu respuesta de error es la señal más importante que recibe un desarrollador. Una respuesta de error bien diseñada permite que un desarrollador entienda qué salió mal, por qué salió mal y qué hacer al respecto, todo dentro de una sola respuesta HTTP. Una respuesta mal diseñada los deja adivinando, abriendo issues y, eventualmente, abandonando tu integración.

Para desarrolladores independientes y equipos pequeños, esto no es un lujo. Es un problema de retención. Cada respuesta de error ambigua es un ticket de soporte que no necesitabas escribir, un desarrollador que se rindió y un golpe a la reputación que se acumula con el tiempo.

El Principio Fundamental: la Consistencia es una Característica

Las respuestas de error consistentes no se trata de ser educado. Se trata de previsibilidad. Cuando cada error sigue la misma estructura, los desarrolladores pueden escribir código de manejo de errores una vez y reutilizarlo en todas partes. Cuando cada error se ve diferente, escriben un manejador nuevo para cada caso borde, y tu API se convierte en una fuente de fricción en lugar de una herramienta.

Esto significa:

La Anatomía de una Buena Respuesta de Error

Una respuesta de error mínima y útil debe contener al menos estos campos:

code — Un identificador legible por máquina y estable para el tipo de error. Este es el campo más importante para el manejo programático. Usa identificadores cortos y descriptivos como invalid_parameter, resource_not_found o rate_limit_exceeded. Evita códigos genéricos como error o fail.

message — Una explicación legible por humanos escrita para el desarrollador, no para el sistema. Esto debe describir qué salió mal en términos que el llamador pueda usar para actuar. “El cuerpo de la solicitud debe incluir un campo email válido” es mejor que “Validación fallida.”

status — El código de estado HTTP. Debe ser preciso y consistente. Un recurso faltante es 404, no 500. Un límite de velocidad es 429, no 400. El código de estado es lo primero que un desarrollador verifica, y ponerlo mal socava cada otro campo en la respuesta.

request_id — Un identificador único para la solicitud fallida. Esto es crítico para la depuración. Cuando un desarrollador reporta un problema, necesitas poder buscar la solicitud exacta en tus registros. Sin un request_id, le estás pidiendo que describa un problema que no puedes reproducir.

details — Datos estructurados opcionales que proporcionan contexto adicional. Esto podría incluir qué parámetro falló la validación, qué valor se proporcionó o cuál era el formato esperado. Mantén esto anidado y acotado para que no se convierta en un lugar de descarga para el estado interno.

Códigos de Estado HTTP: Úsalos Correctamente

Uno de los errores más comunes en el diseño de errores de API es el mal uso de los códigos de estado HTTP. El código de estado debe reflejar la categoría del problema, no el error específico.

Dentro del rango 4xx, sé preciso:

Dentro del rango 5xx, sé honesto:

No devuelvas 500 por errores del cliente. No devuelvas 400 por errores del servidor. Estas no son sugerencias; son el contrato que tu API establece con cada llamador.

Códigos de Error: Estables, Documentados y Significativos

Los códigos de error son cómo los desarrolladores manejan los errores programáticamente. Son el campo que tus llamadores verificarán en sus condicionales. Trátalos con el mismo cuidado que tratarías un contrato de API pública.

Elige códigos que describan el problema, no la implementación. duplicate_email es mejor que constraint_violation_7. insufficient_quota es mejor que billing_error.

Documenta cada código de error en tu referencia de API. Incluye el código, el código HTTP al que mapea, las condiciones que lo activan y un ejemplo de respuesta. Cuando un desarrollador puede encontrar la respuesta a su error sin abrir un ticket de soporte, tu API está haciendo su trabajo.

Qué No Incluir

Las respuestas de error nunca deben filtrar información sensible. Esto incluye:

Un patrón común es devolver un mensaje genérico al llamador mientras se registran los detalles completos del lado del servidor. El llamador recibe "Ocurrió un error inesperado. Por favor contacte a soporte con el ID de solicitud abc-123." y tú registras la excepción real con contexto completo para tu propia depuración.

Respuestas de Error Estructuradas en la Práctica

Así es como se ve una respuesta de error bien diseñada en la práctica:

{
  "status": 422,
  "code": "invalid_parameter",
  "message": "El campo 'email' debe ser una dirección de correo electrónico válida.",
  "request_id": "req_8f3k29d",
  "details": {
    "field": "email",
    "value": "not-an-email",
    "expected_format": "dirección de correo electrónico RFC 5322"
  }
}

Y un error del servidor:

{
  "status": 500,
  "code": "internal_error",
  "message": "Ocurrió un error inesperado. Por favor contacte a soporte con el ID de solicitud req_8f3k29d.",
  "request_id": "req_8f3k29d"
}

Observa la diferencia. El error del cliente le da al desarrollador todo lo que necesita para solucionar el problema. El error del servidor le da un ID de solicitud para que pueda comunicarse, y tú obtienes el contexto completo en tus registros.

Depuración de Errores de API: Qué Necesitan Realmente los Desarrolladores

Cuando un desarrollador está depurando un error de tu API, necesita tres cosas en orden:

  1. Qué pasó — El código de estado HTTP y el código de error le dicen la categoría del problema.
  2. Por qué pasó — El mensaje y los detalles le dicen qué cambiar.
  3. Prueba de que pasó — El ID de solicitud le permite a él y a ti buscar el evento exacto en tus registros.

Si proporcionas las tres, la mayoría de los errores se resuelven sin una sola interacción de soporte. Si solo proporcionas una, estás dejando dos tercios del trabajo de depuración al llamador.

Errores Comunes a Evitar

Devolver texto plano o HTML para errores. Algunas APIs devuelven páginas de error HTML o cadenas de texto plano cuando algo sale mal. Esto obliga a cada llamador a analizar contenido no estructurado. Devuelve JSON, siempre.

Nombres de campos inconsistentes. Un endpoint devuelve error_message, otro devuelve message y un tercero devuelve description. Elige un nombre de campo y úsalo en todas partes.

Sobrecargar el campo message. No coloques datos legibles por máquina en el campo message. El mensaje es para humanos. Los campos code y details son para máquinas.

Ignorar errores reintentables. Algunos errores son transitorios. Los tiempos de espera de red, los límites de velocidad y la disponibilidad temporal del servicio deben usar los códigos de estado apropiados e incluir orientación sobre cómo reintentar. Una respuesta 429 idealmente debería incluir un encabezado Retry-After.

Usar el mismo código de error para problemas diferentes. Si validation_error significa algo diferente en /users que en /orders, no has diseñado una API consistente. Los códigos de error deben ser estables en toda tu superficie de API.

Una Nota sobre el Manejo de Errores en tu Propio Código

Diseñar buenas respuestas de error es solo la mitad del trabajo. También necesitas manejar errores bien en tu propio código. Esto significa capturar errores en el nivel correcto, envolverlos con contexto cuando sea necesario y nunca permitir que excepciones no manejadas escapen al llamador como trazas de pila sin procesar.

En JavaScript, por ejemplo, los errores asíncronos de APIs como IndexedDB no pueden capturarse con try...catch porque no son excepciones sincrónicas. Llegan como eventos o rechazos de promesas, y debes manejarlos a través de los canales de error apropiados. El mismo principio se aplica a los clientes HTTP: los errores de red, los tiempos de espera y las respuestas malformadas llegan a través de mecanismos diferentes, y tu código de manejo de errores debe tener en cuenta cada uno.

Preguntas Frecuentes

¿Debo devolver la misma estructura de error para todos los métodos HTTP? Sí. Ya sea que el error provenga de una solicitud GET, POST, PUT o DELETE, el envoltorio de error debe ser idéntico. La consistencia entre métodos es tan importante como la consistencia entre endpoints.

¿Cuántos códigos de error debo tener? Comienza con los que realmente necesitas. No predefinas códigos de error para problemas que no has encontrado. A medida que tu API crezca, descubrirás nuevas condiciones de error, y está bien. Documentalos cuando aparezcan.

¿Debo versionar mis códigos de error? No. Los códigos de error son parte de tu contrato de API. Cambiar o eliminar un código de error es un cambio rompedor para cualquier llamador que lo verifique. Trata los códigos de error con las mismas garantías de estabilidad que le darías a cualquier otra interfaz pública.

¿Qué hay sobre la localización? Si tu API sirve a desarrolladores en múltiples idiomas, considera admitir un encabezado Accept-Language y devolver mensajes localizados. Sin embargo, los campos code y request_id deben permanecer en inglés y permanecer estables sin importar el idioma. La localización se aplica al mensaje legible por humanos, no a los campos legibles por máquina.

Fuentes