Tratamento de Erros Personalizados no NestJS: Guia Completo para Iniciantes

Posted by

O que é tratamento de erros em uma API

Quando uma aplicação recebe uma requisição, ela pode encontrar problemas durante a execução. Esses problemas podem ser desde dados inválidos até falhas ao se comunicar com o banco de dados.

O tratamento de erros é o processo de identificar essas falhas e responder ao cliente com mensagens claras e úteis, em vez de retornar respostas genéricas como “Internal Server Error”.

Por exemplo, ao tentar buscar um usuário inexistente:

{  
  "message": "Usuário não encontrado",  
  "statusCode": 404  
}  

Essa resposta é muito mais útil do que uma mensagem vaga de erro interno.

Como o NestJS lida com exceções por padrão

O NestJS já fornece mecanismos prontos para lançar exceções HTTP. A principal ferramenta é a classe HttpException, usada para lançar erros com código e mensagem personalizados.

Exemplo:

throw new HttpException('Dados inválidos', HttpStatus.BAD_REQUEST);

Esses erros são automaticamente tratados e enviados ao cliente em formato JSON. No entanto, eles não cobrem exceções não tratadas, como falhas inesperadas no banco de dados.

Criando um filtro global de exceções

Para capturar todos os erros da aplicação — inclusive os não previstos — crie um filtro global de exceções. Ele intercepta qualquer exceção e permite que você controle o formato da resposta.

Exemplo de filtro:

import {
  ExceptionFilter,
  Catch,
  ArgumentsHost,
  HttpException,
  HttpStatus,
} from '@nestjs/common';
import { Request, Response } from 'express';

@Catch()
export class AppExceptionsFilter implements ExceptionFilter {
  catch(exception: unknown, host: ArgumentsHost) {
    const ctx = host.switchToHttp();
    const response = ctx.getResponse<Response>();
    const request = ctx.getRequest<Request>();

    let status = HttpStatus.INTERNAL_SERVER_ERROR;
    let message = 'Erro interno do servidor';

    if (exception instanceof HttpException) {
      status = exception.getStatus();
      const res = exception.getResponse();
      message = typeof res === 'string' ? res : (res as any).message;
    } else if (exception instanceof Error) {
      message = exception.message;
    }

    response.status(status).json({
      message,
      statusCode: status,
      timestamp: new Date().toISOString(),
      path: request.url,
    });
  }
}

Adicione o filtro no main.ts:

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  app.useGlobalFilters(new AppExceptionsFilter());
  await app.listen(3000);
}

Com isso, toda exceção será tratada de forma uniforme.

Estrutura ideal para organizar erros no projeto

Para manter a organização do projeto à medida que ele cresce, recomendamos criar uma pasta exceptions dentro de common, separando filtros, erros customizados e tratamento de banco.

Exemplo de estrutura:

src/
├── common/
│   └── exceptions/
│       ├── filters/
│       │   └── app-exception.filter.ts
│       ├── database/
│       │   ├── postgres.exception.ts
│       │   └── mysql.exception.ts
│       └── custom/
│           ├── not-found.exception.ts
│           └── conflict.exception.ts

Exemplo de exceção customizada:

// common/exceptions/custom/not-found.exception.ts
import { HttpException, HttpStatus } from '@nestjs/common';

export class NotFoundException extends HttpException {
  constructor(message = 'Recurso não encontrado') {
    super({ message }, HttpStatus.NOT_FOUND);
  }
}

Como tratar erros do PostgreSQL e MySQL

Bancos como PostgreSQL e MySQL lançam erros com códigos específicos. É importante tratar esses erros de forma personalizada, como fazemos no AppExceptionsFilter.

Você pode criar um filtro especial ou adicionar lógica no mesmo AppExceptionsFilter para lidar com erros de banco. Veja como adaptar o filtro global:

import { QueryFailedError } from 'typeorm'; // ou do seu ORM
// ...

if (exception instanceof QueryFailedError) {
  const error = exception as any;

  if (error.code === '23505') {
    // PostgreSQL - Unique violation
    status = HttpStatus.CONFLICT;
    message = 'Registro duplicado no PostgreSQL';
  } else if (error.errno === 1062) {
    // MySQL - Duplicate entry
    status = HttpStatus.CONFLICT;
    message = 'Entrada duplicada no MySQL';
  } else {
    message = 'Erro ao acessar o banco de dados';
  }
}

Se quiser, pode extrair isso para uma função reutilizável ou até criar filtros específicos dentro de common/exceptions/database/.

Essa abordagem evita que erros técnicos e códigos de banco cheguem ao cliente.

Como usar throw nos serviços para lidar com falhas esperadas

Nos seus serviços (.service.ts), use throw para lançar exceções esperadas, como ausência de um registro ou dados inválidos. Isso permite que o filtro global formate corretamente a resposta.

Exemplo:

async getUserById(id: number): Promise<User> {
  const user = await this.userRepository.findOne({ where: { id } });

  if (!user) {
    throw new NotFoundException('Usuário não encontrado');
  }

  return user;
}

Outros exemplos úteis:

if (emailEmUso) {
  throw new ConflictException('Email já cadastrado');
}

Essas exceções são capturadas pelo filtro global e retornam respostas padronizadas ao cliente.

Conclusão

Adotar tratamento de erros personalizado no NestJS torna sua API mais segura, confiável e profissional. Além de melhorar a experiência do desenvolvedor, também fornece respostas mais úteis aos consumidores da API.

Ao seguir essas boas práticas:

  • Você centraliza o controle de erros
  • Evita expor detalhes internos do sistema
  • Facilita a manutenção e o crescimento do projeto

Leave a Reply

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *