A forma como lidamos com erros distingue um código amador de uma aplicação pronta para produção. Um erro não tratado pode derrubar seu servidor Node.js, deixar a interface do usuário em um estado inconsistente ou, pior, corromper dados silenciosamente. Um tratamento de erros eficaz implica em antecipar falhas, gerenciar imprevistos com elegância e fornecer feedback claro, tanto para os usuários quanto para os desenvolvedores, sobre a natureza do problema.

try/catch/finally: O Pilar Essencial para a Gestão de Erros

Tratamento Básico de Erros
try {
  const data = JSON.parse(userInput);
  processData(data);
} catch (error) {
  // Lida com o erro
  console.error("Falha ao analisar entrada:", error.message);
  showUserMessage("Entrada inválida. Por favor, verifique seus dados.");
} finally {
  // Sempre executa — código de limpeza
  hideLoadingSpinner();
}

Compreendendo os Tipos de Erro Nativos do JavaScript

Tipos de Erro Nativos
// SyntaxError — JSON inválido, código malformado
JSON.parse("{invalid}");

// TypeError — tipo incorreto, tentando acessar propriedade de null/undefined
null.toString();
undefined.map(x => x);

// ReferenceError — usando uma variável não declarada
console.log(nonExistentVariable);

// RangeError — valor fora do intervalo permitido
new Array(-1);

// URIError — URI malformada
decodeURIComponent("%");

// Verifica o tipo do erro
try {
  riskyOperation();
} catch (error) {
  if (error instanceof TypeError) {
    // Trata erros de tipo especificamente
  } else if (error instanceof SyntaxError) {
    // Trata erros de sintaxe
  } else {
    throw error; // Relança erros inesperados
  }
}

Classes de Erro Personalizadas: Aprimorando a Comunicação e o Controle

Erros Personalizados
class AppError extends Error {
  constructor(message, statusCode, code) {
    super(message);
    this.name = 'AppError';
    this.statusCode = statusCode;
    this.code = code;
    this.isOperational = true;
  }
}

class NotFoundError extends AppError {
  constructor(resource = 'Resource') {
    super(`${resource} não encontrado`, 404, 'NOT_FOUND');
    this.name = 'NotFoundError';
  }
}

class ValidationError extends AppError {
  constructor(field, message) {
    super(`Validação falhou: ${message}`, 400, 'VALIDATION_ERROR');
    this.name = 'ValidationError';
    this.field = field;
  }
}

// Exemplo de Uso
function getUser(id) {
  const user = db.findUser(id);
  if (!user) throw new NotFoundError('User');
  return user;
}

Gestão de Erros Assíncronos: Dominando Promises e Async/Await

Erros em Async/Await
// ✅ Correto — try/catch com async/await
async function fetchUser(id) {
  try {
    const response = await fetch(`/api/users/${id}`);

    if (!response.ok) {
      throw new AppError(
        `HTTP ${response.status}: ${response.statusText}`,
        response.status,
        'HTTP_ERROR'
      );
    }

    return await response.json();
  } catch (error) {
    if (error instanceof AppError) {
      throw error; // Relança erros conhecidos
    }
    // Encapsula erros desconhecidos
    throw new AppError('Erro de rede', 500, 'NETWORK_ERROR');
  }
}

// ❌ Incorreto — rejeição de promise não tratada
async function bad() {
  const data = await fetch('/api/data'); // Se isso lançar um erro, ele não será tratado!
}

Gerenciadores de Erros Globais: A Rede de Segurança Final da Aplicação

Captura Abrangente de Erros
// Navegador — captura erros não tratados
window.addEventListener('error', (event) => {
  console.error('Erro não capturado:', event.error);
  // Envia para o serviço de rastreamento de erros
});

window.addEventListener('unhandledrejection', (event) => {
  console.error('Rejeição de promise não tratada:', event.reason);
  event.preventDefault(); // Previne o tratamento padrão do navegador
});

// Node.js — captura erros não tratados
process.on('uncaughtException', (error) => {
  console.error('Exceção não capturada:', error);
  process.exit(1); // Sai — o processo está em um estado desconhecido
});

process.on('unhandledRejection', (reason) => {
  console.error('Rejeição não tratada:', reason);
  // No Node 15+, isso encerra o processo por padrão
});

Middleware de Erros no Express.js: Centralizando e Padronizando Respostas

Gerenciador de Erros Centralizado
// Middleware de tratamento de erros (4 parâmetros)
app.use((err, req, res, next) => {
  const statusCode = err.statusCode || 500;
  const message = err.isOperational ? err.message : 'Erro interno do servidor';

  // Registra o erro completo
  console.error(`[${err.code}] ${err.message}`, err.stack);

  // Envia resposta "limpa" ao cliente
  res.status(statusCode).json({
    error: {
      message,
      code: err.code || 'INTERNAL_ERROR',
      ...(process.env.NODE_ENV === 'development' && { stack: err.stack })
    }
  });
});

Boas Práticas Essenciais para um Tratamento de Erros Robusto

  • **Nunca ignore erros silenciosamente:** Blocos `catch` vazios são uma das piores armadilhas, pois mascaram bugs e impedem a depuração. Sempre registre ou relance o erro.
  • **Utilize classes de erro personalizadas:** Diferencie erros operacionais (como validação de entrada, recurso não encontrado) de erros de programação (bugs inesperados). Isso facilita a lógica de tratamento e a resposta ao usuário.
  • **Sempre relance erros que você não consegue tratar:** Deixe que os erros "subam" na cadeia de chamadas até um manipulador capaz de processá-los adequadamente ou, em último caso, para um manipulador global.
  • **Registre erros com contexto:** Um log útil inclui informações como ID do usuário, URL da requisição, dados de entrada relevantes e o stack trace completo. Isso é vital para diagnosticar a causa raiz.
  • **Utilize um serviço de rastreamento de erros em produção:** Ferramentas como Sentry, Datadog ou LogRocket são indispensáveis para coletar, agregar e alertar sobre erros em tempo real, fornecendo insights valiosos.
  • **Falhe de forma elegante:** Em vez de travar ou exibir uma página em branco, forneça ao usuário uma mensagem de erro compreensível e sugestões sobre o que fazer em seguida, melhorando a experiência do usuário.

Experimente Nossas Ferramentas Gratuitas de JavaScript

Depure e formate seu código JavaScript instantaneamente com facilidade.