← Servidor web y API con Node

Sesión 9 · Semana 3

El contrato de errores en la práctica

Hoy · Hoja de ruta

  1. 1. Aprende: Cómo se traduce cada error interno a una respuesta HTTP, en un solo sitio.
  2. 2. Haz: Cierra el manejador central y comprueba todos los casos.
  3. 3. Comprueba: Ninguna ruta decide ya un código de estado por su cuenta.

Antes de empezar · 5 minutos, sin apuntes

  1. ¿Cuántos sitios de tu código deciden hoy un código de estado?
  2. Si añades un tipo de error nuevo, ¿cuántos ficheros tocas?
  3. ¿Qué debe ver el cliente cuando el fallo es tuyo?

La traducción, en un solo sitio

const ESTADOS = {
  ErrorDeValidacion: 400,
  ErrorNoAutenticado: 401,
  ErrorSinPermiso: 403,
  ErrorNoEncontrado: 404,
  ErrorDeConflicto: 409
};

export function manejadorDeErrores(error, peticion, respuesta, next) {
  const estado = ESTADOS[error.name] ?? 500;

  if (estado >= 500) {
    console.error(`[${peticion.id}] ${peticion.method} ${peticion.originalUrl}`, error);
  }

  respuesta.status(estado).json({
    error: estado >= 500 ? "Error interno del servidor" : error.message,
    codigo: error.codigo ?? error.name ?? "ERROR",
    detalles: error.detalles ?? [],
    peticion: peticion.id
  });
}

Añadir un tipo de error nuevo se reduce a añadir una línea a la tabla, sin que ninguna ruta necesite conocer qué código corresponde a su fallo.

El identificador de petición

export function identificar(peticion, respuesta, next) {
  peticion.id = crypto.randomUUID();
  respuesta.setHeader("X-Request-Id", peticion.id);
  next();
}

Un identificador convierte «me da error» en un caso investigable

El cliente ve un mensaje genérico y un identificador. Ese mismo identificador está en tus registros junto a la traza completa. Quien reporta el problema te da el número, y tú encuentras exactamente su petición entre miles.

Es lo que permite no filtrar detalles internos sin quedarte ciego para diagnosticar.

El cliente, del otro lado

async function pedir(url, opciones) {
  const respuesta = await fetch(url, opciones);
  if (respuesta.ok) return respuesta.status === 204 ? null : respuesta.json();

  const cuerpo = await respuesta.json().catch(() => ({}));
  throw new ErrorDeApi(respuesta.status, cuerpo.error ?? "Error inesperado", cuerpo.detalles ?? []);
}

Aquí se cobra el contrato: una sola función en el cliente sirve para toda la API, hoy y cuando añadas rutas. Si cada error tuviera una forma distinta, esta función no podría existir.

Tarea 9 · Errores de punta a punta

  1. Define los cinco tipos de error de tu aplicación.
  2. Escribe la tabla de traducción y el manejador central.
  3. Añade el identificador de petición y sácalo en registro y respuesta.
  4. Elimina todos los códigos de estado repartidos por las rutas.
  5. Escribe la función pedir en el cliente y úsala en toda la interfaz.
  6. Provoca los cinco errores desde el cliente y comprueba qué se ve y qué se registra.
Objetivo mínimoManejador central, identificador y cliente con función única.
Si lo tienesMuestra en el formulario los detalles de validación, campo por campo.
RetoAñade un tipo de error nuevo y comprueba que solo tocas la tabla.

Cierre de la semana 3

  • Las tres capas están separadas de verdad.
  • Puedes cambiar de almacén sin tocar rutas ni servicio.
  • Los errores se traducen a HTTP en un solo sitio.
  • El cliente trata todos los errores con una sola función.
Ver respuestas

1 · En el manejador central de errores, con una tabla de traducción.

2 · Para poder relacionar lo que ve el cliente con la traza de tus registros.

3 · Un mensaje genérico: el detalle se queda en el servidor.

Microprueba semanal 3 · 5–10 minutos

Individual, sin IA y sin apuntes.

  1. Di a qué capa pertenece cada cosa: escribir un 404, «no se puede borrar con stock», y leer el fichero de datos.
  2. ¿Cómo compruebas que tus capas están separadas de verdad?
  3. ¿Por qué el cliente nunca debe ver la traza de un error?
---