Erros com significado
Em vez de lançar Error "pelados", define-se uma classe de erro
personalizada que carrega informação útil, como o código de status HTTP.
class ApiError extends Error {
constructor(mensagem, statusCode) {
super(mensagem); // define this.message
this.name = "ApiError";
this.statusCode = statusCode;
}
}
Como ApiError estende Error, ele continua sendo instanceof Error, então
qualquer código que já trate erros genéricos o entende, e além disso
expõe statusCode para que o middleware de erro escolha a resposta.
if (!usuario) {
throw new ApiError("Usuário não encontrado", 404);
}
Operacionais vs de programação
Distinguir duas famílias de erros ajuda a decidir como responder:
- Operacionais (esperados): entrada inválida, recurso inexistente, sem
permissões. Fazem parte normal do funcionamento. Levam um
statusCode4xx e uma mensagem pensada para o cliente. São tratados e respondidos com clareza. - De programação (bugs): um
undefinedque não deveria existir, uma chamada a uma função inexistente. São falhas do código. São respondidos de forma genérica com 5xx ("Erro interno") para não vazar detalhes, e são registrados para serem corrigidos.
Uma boa regra: só os erros operacionais devem levar mensagem e status pensados para o usuário; os de programação ficam escondidos atrás de um 500.
Exemplos
Um ApiError continua sendo um Error
class ApiError extends Error {
constructor(mensagem, statusCode) {
super(mensagem);
this.name = "ApiError";
this.statusCode = statusCode;
}
}
const e = new ApiError("Não autorizado", 401);
console.log(e.message); // Não autorizado
console.log(e.statusCode); // 401
console.log(e instanceof Error); // true