4.4 · Validación, gestión de errores y tests (Jest)

Nivel 4 · API, autenticación y tiempo real

4.4Validación, gestión de errores y tests (Jest)

Objetivo: Fiabilizar una API : validación de las entradas (Zod/Joi), gestión de errores centralizada y limpia, tests automatizados (Jest).
Tiempo estimado: 12 min · lectura autónoma · Si solo tienes 5 min: lee «Lo esencial en 30s» + el esquema.

Lo esencial en 30 segundos

  • No confiar nunca en las entradas : validar (Zod/Joi) antes de tratar, rechazar en 400 claro — contra datos corruptos, bugs y fallos.
  • Centralizar los errores : middleware Express (err, req, res, next) en último, try/catch sobre los await, 404/400 esperados vs 500 inesperados.
  • Reflejo de Salim : testear con Jest (unitario + ruta) y relanzar tras cada modificación — la no-regresión que libera.

Mini-caso hilado — Salim fiabiliza antes de la puesta en producción

La API de Salim (4.1-4.3) funciona en demo, pero un vendedor crea un producto sin nombre, una ruta falla sobre un id desconocido y nadie sabe qué testear antes de cada entrega. Salim añade un esquema Zod que rechaza los cuerpos inválidos en 400, un middleware de errores centralizado que responde limpiamente sin filtrar detalles, luego escribe sus primeros tests Jest : una función testeada aisladamente, una ruta testeada de extremo a extremo. En la próxima modificación, relanza la suite en pocos segundos. Último hito del hilo : del prototipo a la aplicación profesional.

Último episodio del caso 4.1-4.3 — Salim cierra el nivel : construir, asegurar, animar, fiabilizar.

1. La validación de las entradas

Recordatorio de 4.2 : no confiar nunca en las entradas. Antes de tratar los datos de un cliente (cuerpo, parámetros), verifique que tienen el formato esperado : campos requeridos presentes, tipos correctos, restricciones respetadas (email válido, longitud máxima). Si son inválidos : rechazo en 400 con un mensaje claro, antes de todo tratamiento. Librerías : Zod (moderno, excelente con TypeScript — un esquema que valida y tipa) o Joi (probado). La validación protege contra los datos corruptos, los bugs y ciertos fallos — indispensable para toda entrada externa.

2. La gestión de errores centralizada

En producción, ocurren errores : base no disponible, recurso no encontrado, operación fallida. En Express, el patrón es un middleware de gestión de errores centralizado — función especial de 4 parámetros (err, req, res, next), colocada en último : los errores se dirigen ahí y devuelve una respuesta coherente (código correcto, mensaje claro, sin detalles sensibles). Se rodean los await con try/catch (o wrapper) y se distinguen los errores esperados (404, 400) de los inesperados (500).

3. Los tests automatizados con Jest

En vez de verificar a mano (fastidioso, incompleto, no repetible), se escribe código que testea el código. Jest es el framework más popular en Node : unitarios (una función aislada), integración (varias partes, una ruta con su base), de extremo a extremo. Un test verifica que una entrada da la salida esperada (expect(resultat).toBe(attendu)). Beneficios : confianza, no-regresión (relanzar tras cada cambio), documentación del comportamiento, mejor diseño (el código testeable está mejor estructurado). Validación + errores + tests : lo que separa un prototipo de una app profesional.

Error frecuente de interpretación: « Testear es perder el tiempo : mi código funciona, lo verifiqué a mano ». No. La verificación manual solo cubre algunos casos, solo hoy — y cada futura modificación puede romper silenciosamente lo que funcionaba (regresión). Los tests se relanzan en segundos tras cada cambio y alertan enseguida : es esa no-regresión la que permite modificar y refactorizar sin miedo. « Funciona » sin tests es « funciona por ahora ».
El camino de una petición fiabilizada Validar → tratar → gestionar → testear Validación Esquema Zod/Joi,400 si es inválidoTratamiento Lógica +try/catch sobre awaitErrores Middlewarecentralizado en últimoTests Jest : unitario +ruta, relanzados
Validar → tratar → gestionar → testear
Versión en texto del esquema (SEO / lectores de pantalla / traducción): 1) Salim valida cada cuerpo de petición con un esquema : inválido → 400 claro, antes de todo tratamiento. 2) Rodea sus await con try/catch y dirige los errores hacia un middleware centralizado colocado en último. 3) Ese middleware responde limpiamente : 404/400 para lo esperado, 500 sin detalles sensibles para lo inesperado. 4) Cubre todo con tests Jest relanzados en cada modificación. Lección : validar, centralizar, testear.

🔎 Ejemplo concreto — 🔎 Ejemplo concreto — Validar y luego testear

Los dos gestos de Salim sobre la creación de producto :

Esquema : { nom: cadena no vacía, prix: número positivo }
→ cuerpo inválido : 400 { erreur: 'nom requerido, prix positivo esperado' }
Test : POST /produits sin nom → se espera 400
Test : función total(panier) → expect(total).toBe(300)

Lectura : el esquema rechaza antes del tratamiento, el test bloquea el comportamiento.

Lección : 400 claro para el cliente, tests en verde para el equipo.

Vocabulario de la sección

Validación de las entradas
Verificar que los datos recibidos tienen el formato esperado (campos, tipos, restricciones) antes del tratamiento ; rechazar en 400 si son inválidos. Herramientas : Zod, Joi.

Ejemplo sencillo: El esquema de Salim rechaza un producto sin nombre antes de toda escritura.

Lo que no es: tratar directamente req.body sin ningún control.

Reflejo: ninguna entrada externa toca los datos sin esquema.

Middleware de errores
Función Express especial (err, req, res, next) colocada en último, que centraliza los errores y devuelve una respuesta coherente.

Ejemplo sencillo: En casa de Salim, todo error termina ahí : código correcto, mensaje claro, sin fuga sensible.

Lo que no es: try/catch dispersos que devuelven cada uno un formato distinto.

Reflejo: un solo formato de error para toda la API.

Esperado vs inesperado
Esperados : 404 no encontrado, 400 inválido — gestionados explícitamente. Inesperados : bugs → 500 sin detalles sensibles.

Ejemplo sencillo: Salim devuelve 404 para un producto desconocido, 500 genérico para un bug.

Lo que no es: una stack trace en bruto devuelta al cliente en producción.

Reflejo: el cliente recibe el código correcto, nunca nuestros secretos.

Test automatizado (Jest)
Código que verifica el código : entrada dada → salida esperada (expect().toBe()). Unitarios, integración, de extremo a extremo.

Ejemplo sencillo: Salim testea su función de total y luego su ruta POST.

Lo que no es: una verificación manual única en el navegador.

Reflejo: escribir el test que prueba que funciona.

No-regresión
Relanzar los tests tras cada modificación para detectar lo que se rompió sin querer.

Ejemplo sencillo: Salim relanza su suite antes de cada entrega y duerme tranquilo.

Lo que no es: descubrir la rotura en producción por un ticket cliente.

Reflejo: suite en verde exigida antes de cada merge.

En la práctica — En práctica — Fiabilizar una API (validación, errores, tests)

  1. Valide las entradas de una ruta con Zod o Joi : defina un esquema, rechace lo inválido en 400 con un mensaje claro.
  2. Ponga el middleware de errores centralizado (err, req, res, next) en último ; rodee los await con try/catch.
  3. Distinga errores esperados (404, 400) e inesperados (500) ; no divulgue ningún detalle sensible en producción.
  4. Escriba tests Jest : un unitario (expect().toBe()) y uno de ruta ; reláncelos tras cada modificación.
Fiabiliza su API : validación (Zod/Joi), errores centralizados y limpios, tests Jest — lo que separa un prototipo de una aplicación profesional.

Guion listo para decir — anunciar la fiabilización (30 s)

« Soy Salim : antes de la puesta en producción, cada entrada es validada por esquema con rechazo en 400 claro, todos los errores pasan por un middleware centralizado que no filtra ningún detalle, y la suite Jest cubre lo unitario como las rutas — relanzada en cada modificación para la no-regresión. »

Para decir antes de cada puesta en producción ; muestre la suite en verde.

Modelo copiar-pegar — validación Zod + errores + test Jest (código probado)

// 1) Validación (npm i zod) + 2) errores centralizados
const express = require('express');
const { z } = require('zod');

const app = express();
app.use(express.json());

const schemaProduit = z.object({
  nom: z.string().min(1, 'nom requerido'),
  prix: z.number().positive('prix positivo esperado')
});

const produits = [];

app.post('/api/v1/produits', (req, res, next) => {
  try {
    const donnees = schemaProduit.parse(req.body); // lanza si es inválido
    const produit = { id: produits.length + 1, ...donnees };
    produits.push(produit);
    res.status(201).json(produit);
  } catch (err) {
    err.statut = 400; // esperado : entrada inválida
    next(err);
  }
});

// Middleware de errores : SIEMPRE en último
// eslint-disable-next-line no-unused-vars
app.use((err, req, res, next) => {
  const statut = err.statut || 500;
  const message = statut === 500 ? 'Error interno' : err.message;
  res.status(statut).json({ erreur: message });
});

module.exports = app;

// 3) Test Jest (archivo app.test.js, npm i -D jest supertest) :
// const request = require('supertest');
// const app = require('./app');
// test('POST sin nom → 400', async () => {
//   const res = await request(app).post('/api/v1/produits').send({ prix: 10 });
//   expect(res.status).toBe(400);
// });
// test('POST válido → 201', async () => {
//   const res = await request(app).post('/api/v1/produits').send({ nom: 'Couscoussier', prix: 2500 });
//   expect(res.status).toBe(201);
//   expect(res.body.nom).toBe('Couscoussier');
// });

Prototipo de aprendizaje

Validación mínima + 2-3 tests sobre lo crítico : formador sin frenar la exploración.

API real en producción

Como Salim : esquema sobre cada entrada externa, middleware centralizado, suite Jest exigida en verde antes del merge.

Proyecto TypeScript

Zod valida Y tipa : un solo esquema para la seguridad runtime y la robustez compile-time.

Equipo que descubre los tests

Empezar por las rutas críticas (creación, conexión) : valor inmediato, hábito instalado.

Prudencia: La fiabilidad compromete su credibilidad : valide toda entrada externa y nunca devuelva detalles técnicos sensibles (trazas, peticiones, secretos) en las respuestas de error en producción — mensaje genérico en 500. Los ejemplos de esta guía usan datos ficticios únicamente. Contenido pedagógico, no una auditoría de calidad.

✓ Buenas prácticas

  • Validar antes de tratar, rechazar en 400 claro.
  • Centralizar los errores en un middleware único.
  • Testear y relanzar tras cada modificación.

✗ Errores frecuentes

  • Tratar req.body sin validación.
  • Devolver la stack trace al cliente.
  • Solo testear a mano, una vez.

Puntos clave

  • VALIDACIÓN (Zod/Joi) : verificar formato/tipos/restricciones ANTES del tratamiento, 400 claro si es inválido. No confiar nunca en el cliente.
  • ERRORES centralizados : middleware (err, req, res, next) en último → respuesta coherente ; gestionar el ASYNC (try/catch).
  • TESTS JEST : unitarios + integración (ruta). expect(resultat).toBe(attendu).
  • Beneficios : CONFIANZA, NO-REGRESIÓN, documentación, mejor diseño. El paso prototipo → profesional.

Reto 48h — aplica mañana

Añada un esquema de validación a su ruta más crítica, el middleware de errores, y 2 tests Jest (1 rechazo 400, 1 éxito) — suite en verde exigida.

Lista de salida de la sección
  • Puedo explicar validación, middleware de errores, Jest.
  • Sé escribir un 400 claro y un 500 sin fuga.
  • Tengo 2 tests en verde sobre mi ruta crítica.
Autoevaluación 1-5 : ¿cada entrada externa está validada?¿mi suite pasa tras mi última modificación?

Preguntas frecuentes

¿Por qué testear cuando « ya funciona »?

Porque « funciona » solo vale para los casos verificados a mano, hoy. Mañana, cada modificación puede romper silenciosamente otra cosa (regresión) — y sin tests, habría que volver a verificarlo todo a mano cada vez, lo que nunca se hace. Los tests aportan confianza (se sabe, no se cree), no-regresión (alerta inmediata tras cada cambio), documentación del comportamiento esperado, y empujan a estructurar mejor el código testeable.

Validación, errores, tests : ¿hace falta todo, para todos los proyectos?

Dose según lo que esté en juego. Prototipo desechable : aligere para ir rápido, pero practique un mínimo (buenos hábitos). Aplicación real (usuarios, producción, equipo) : indispensable — validación (datos corruptos, bugs, fallos), errores (si no el servidor falla o filtra detalles), tests (si no las regresiones pasan hasta producción). Estas tres prácticas transforman un prototipo en aplicación fiable.

Comprueba tu comprensión

¿Dónde colocar el middleware de gestión de errores Express?

Recursos adicionales (opcional, para ir más allá)

Examen del nivel 4Preguntas inéditas y más difíciles para validar tus conocimientos