Introdução: Por que o tratamento de erros é a base de um código confiável
Qualquer aplicação rodando em Node.js, mais cedo ou mais tarde, enfrenta situações excepcionais: entrada inválida do usuário, falha de rede, arquivo ausente no disco ou dados incorretos do banco. Sem um tratamento de erros adequado, essas situações levam à queda do servidor, vazamento de memória ou comportamento indefinido. Em JavaScript, especialmente no ambiente Node.js, o mecanismo try/catch é uma ferramenta fundamental para construir sistemas tolerantes a falhas. Neste artigo, abordaremos não apenas o clássico try/catch, mas também técnicas avançadas: classes de erro personalizadas, trabalho com promises e funções assíncronas, além das melhores práticas de logging.
Fundamentos: blocos try, catch e finally
A sintaxe básica do tratamento de erros em JavaScript é a seguinte:
try { // Código que pode lançar uma exceção const data = JSON.parse('{"name": "Node.js"}'); console.log(data.name);} catch (error) { // Tratamento do erro console.error('Ocorreu um erro:', error.message);} finally { // Código que será executado de qualquer forma console.log('Bloco finally executado');}Como funciona:
try— contém o código potencialmente perigoso.catch— captura a exceção, se ocorrer. O parâmetroerrorrecebe o objeto de erro.finally— é executado sempre, independentemente de ter havido erro ou não. Ideal para fechar arquivos, liberar recursos ou finalizar transações.
Importante lembrar: finally será executado mesmo após um return no bloco try ou catch.
Tipos de erros em JavaScript
Os construtores de erro nativos ajudam a classificar as exceções com mais precisão:
Error— classe base para todos os erros.SyntaxError— erro de sintaxe (por exemplo, JSON inválido).TypeError— tipo de dado incorreto (por exemplo, chamar algo que não é uma função).ReferenceError— referência a uma variável inexistente.RangeError— valor fora dos limites permitidos (por exemplo, recursão).
Exemplo de tratamento de diferentes tipos:
try { const obj = null; console.log(obj.property); // TypeError} catch (err) { if (err instanceof TypeError) { console.log('Erro de tipo:', err.message); } else if (err instanceof ReferenceError) { console.log('Variável não encontrada'); } else { console.log('Erro desconhecido:', err); }}Gerando seus próprios erros: operador throw
Às vezes, é necessário indicar explicitamente que ocorreu uma situação excepcional no programa. Para isso, usa-se o operador throw. Ele pode lançar qualquer coisa: uma string, um número, um objeto, mas o ideal é uma instância da classe Error ou de suas subclasses.
function divide(a, b) { if (b === 0) { throw new Error('Divisão por zero não é permitida'); } if (typeof a !== 'number' || typeof b !== 'number') { throw new TypeError('Os argumentos devem ser números'); } return a / b;}
try { const result = divide(10, 0); console.log(result);} catch (err) { console.error(err.name + ': ' + err.message);}Por que é melhor lançar um Error em vez de uma string? O objeto Error contém a stack de chamadas (stack), o que é crucial para a depuração em Node.js. Uma string não fornece essa informação.
Criando classes de erro personalizadas
Em projetos reais (por exemplo, um servidor web com Express), é conveniente ter seus próprios tipos de erro para diferentes situações: ValidationError, NotFoundError, DatabaseError. Isso simplifica o tratamento centralizado.
class ValidationError extends Error {