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