Valida los datos en el borde de la API y centraliza toda la gestión de errores en un único middleware de cuatro parámetros.
Abrir esta lección en KodokonCada dato entrante es hostil hasta que se demuestre lo contrario: cuerpo JSON, parámetros de URL, cabeceras. La validación pertenece al borde de la aplicación, antes del controlador, para que las capas internas trabajen sobre datos garantizados. Después, distingue dos familias de errores: los errores operacionales (entrada inválida, recurso ausente, conflicto), esperados y traducidos a 4xx, y los errores de programación (bugs), que deben producir un 500 opaco del lado del cliente y una traza de pila completa del lado del servidor.
export class HttpError extends Error {
constructor(status, message, details = undefined) {
super(message);
this.status = status;
this.details = details;
}
}Un middleware de validación intercepta la petición antes del controlador. La versión manual de abajo muestra el principio; en producción, un esquema declarativo (zod se ha convertido en el estándar) aporta además inferencia de tipos y mensajes estructurados. La contrapartida: una dependencia y un ligero coste en tiempo de ejecución, a cambio de reglas centralizadas, componibles e imposibles de olvidar.
import { HttpError } from './http-error.js';
export function validateUser(req, res, next) {
const { email, name } = req.body ?? {};
const errors = [];
if (typeof email !== 'string' || !email.includes('@')) {
errors.push('email: invalid format');
}
if (typeof name !== 'string' || name.trim().length < 2) {
errors.push('name: 2 characters minimum');
}
if (errors.length > 0) {
return next(new HttpError(400, 'Invalid body', errors));
}
next();
}Express reconoce un middleware de errores por su firma de cuatro parámetros (err, req, res, next). Registrado el último, después de todas las rutas con app.use(errorHandler), se convierte en la única salida de errores de la API: un formato de respuesta homogéneo, el logging en un solo lugar, sin fugas de información interna. Los controladores, por su parte, se limitan a llamar a next(err).
export function errorHandler(err, req, res, next) {
if (res.headersSent) {
return next(err);
}
const status = err.status ?? 500;
const body = { error: err.message };
if (err.details) {
body.details = err.details;
}
if (status >= 500) {
console.error(err);
body.error = 'Internal server error';
}
res.status(status).json(body);
}