Errores con significado
En lugar de lanzar Error "pelados", se define una clase de error
personalizada que lleva información útil, como el código de estado HTTP.
class ApiError extends Error {
constructor(mensaje, statusCode) {
super(mensaje); // fija this.message
this.name = "ApiError";
this.statusCode = statusCode;
}
}
Como ApiError extiende Error, sigue siendo instanceof Error, así
que cualquier código que ya maneje errores genéricos lo entiende, y además
expone statusCode para que el middleware de error elija la respuesta.
if (!usuario) {
throw new ApiError("Usuario no encontrado", 404);
}
Operacionales vs de programación
Distinguir dos familias de errores ayuda a decidir cómo responder:
- Operacionales (esperados): entrada inválida, recurso inexistente, sin
permisos. Son parte normal del funcionamiento. Llevan un
statusCode4xx y un mensaje pensado para el cliente. Se manejan y se responden con claridad. - De programación (bugs): un
undefinedque no debería existir, una llamada a una función inexistente. Son fallos del código. Se responden de forma genérica con 5xx ("Error interno") para no filtrar detalles, y se registran para corregirlos.
Una buena regla: solo los errores operacionales deben llevar mensaje y estado pensados para el usuario; los de programación se ocultan tras un 500.
Ejemplos
Un ApiError sigue siendo un Error
class ApiError extends Error {
constructor(mensaje, statusCode) {
super(mensaje);
this.name = "ApiError";
this.statusCode = statusCode;
}
}
const e = new ApiError("No autorizado", 401);
console.log(e.message); // No autorizado
console.log(e.statusCode); // 401
console.log(e instanceof Error); // true