Cincoders

Tratamento de Erros & Exceções

Tratamento de Erros & Exceções

O boilerplate padroniza todas as respostas de erro da API através de uma hierarquia customizada de exceções de domínio e um filtro global de exceções que formata a saída conforme a RFC 7807 (Problem Details for HTTP APIs).


🛑 Formato Padrão de Resposta de Erro (RFC 7807)

Toda resposta HTTP com status de erro (4xx ou 5xx) é retornada com Content-Type: application/problem+json e a seguinte estrutura JSON:

{
  "type": "https://cincoders.example/problems/not-found",
  "title": "Recurso não encontrado",
  "status": 404,
  "detail": "Tarefa com id \"d3b07384-d113-4f9e-b839-44589d978a3c\" não encontrado(a).",
  "instance": "/cincoders/api/tasks/d3b07384-d113-4f9e-b839-44589d978a3c",
  "code": "NOT_FOUND",
  "timestamp": "2026-08-26T20:15:00.000Z"
}
CampoSignificado
typeURI estável identificando a classe do erro: https://cincoders.example/problems/<code-em-kebab-case>.
titleTítulo humano, curto e fixo por ErrorCode (não varia entre ocorrências do mesmo tipo de erro).
statusCódigo de status HTTP, repetido no corpo por conveniência do cliente.
detailMensagem específica desta ocorrência do erro (pode variar por instância — ex: inclui o id do recurso).
instancePath da requisição que originou o erro.
codeCódigo de máquina do ErrorCode (mesmo enum usado internamente, ver abaixo).
errorsPresente apenas em erros de validação (422): mapa { campo: string[] } com as mensagens agrupadas por campo.

O shape é documentado no Swagger pela classe ProblemDetails (src/common/filters/problem-details.dto.ts).

Respostas de sucesso continuam usando o envelope { statusCode, timestamp, path, data } do TransformInterceptor — a RFC 7807 se aplica apenas a respostas de erro.


🏷️ Enum de Códigos de Erro: ErrorCode

O enum ErrorCode (src/common/exceptions/error-code.enum.ts) define os códigos semânticos de máquina retornados no campo code:

// src/common/exceptions/error-code.enum.ts
export enum ErrorCode {
  NOT_FOUND = 'NOT_FOUND',
  VALIDATION_FAILED = 'VALIDATION_FAILED',
  ACCESS_DENIED = 'ACCESS_DENIED',
  CONFLICT = 'CONFLICT',
  EXTERNAL_SERVICE_UNAVAILABLE = 'EXTERNAL_SERVICE_UNAVAILABLE',
  INTERNAL_ERROR = 'INTERNAL_ERROR',
}

🧱 Hierarquia de Exceções: AppException

Todas as exceções específicas de regras de negócio estendem a classe base AppException (src/common/exceptions/app.exception.ts):

// src/common/exceptions/app.exception.ts
import { HttpException, type HttpStatus } from '@nestjs/common';
import type { ErrorCode } from './error-code.enum';

export abstract class AppException extends HttpException {
  constructor(
    private readonly errorCode: ErrorCode,
    message: string,
    status: HttpStatus,
  ) {
    super({ message }, status);
  }

  getErrorCode(): ErrorCode {
    return this.errorCode;
  }
}

Exceções Pré-definidas

  1. ResourceNotFoundException (HTTP 404):

    throw new ResourceNotFoundException('Tarefa', id);
    // Mensagem gerada: "Tarefa com id \"...\" não encontrado(a)."
    // Código: ErrorCode.NOT_FOUND
  2. AccessDeniedException (HTTP 403):

    throw new AccessDeniedException('Você não possui privilégios para executar esta ação.');
    // Código: ErrorCode.ACCESS_DENIED

🔍 Erros de Validação do Zod

Quando o ZodValidationPipe rejeita um payload, ele lança ZodValidationException (ou ZodError).

O HttpExceptionFilter intercepta o erro e agrupa as mensagens por campo no campo errors, retornando status HTTP 422 Unprocessable Entity:

{
  "type": "https://cincoders.example/problems/validation-failed",
  "title": "Dados inválidos",
  "status": 422,
  "detail": "Um ou mais campos precisam ser corrigidos.",
  "instance": "/cincoders/api/tasks",
  "code": "VALIDATION_FAILED",
  "timestamp": "2026-08-26T20:15:00.000Z",
  "errors": {
    "title": ["String must contain at least 3 character(s)"],
    "priority": ["Invalid enum value. Expected 'LOW' | 'MEDIUM' | 'HIGH' | 'URGENT', received 'URGENTE'"]
  }
}

➕ Como Criar uma Nova Exceção de Domínio

Para adicionar uma nova exceção customizada (por exemplo, conflito de e-mail duplicado):

  1. Crie o arquivo em src/common/exceptions/<nome>.exception.ts.
  2. Estenda AppException definindo o ErrorCode e o HttpStatus:
// src/common/exceptions/email-already-in-use.exception.ts
import { HttpStatus } from '@nestjs/common';
import { AppException } from './app.exception';
import { ErrorCode } from './error-code.enum';

export class EmailAlreadyInUseException extends AppException {
  constructor(email: string) {
    super(
      ErrorCode.CONFLICT,
      `O e-mail "${email}" já está cadastrado no sistema.`,
      HttpStatus.CONFLICT,
    );
  }
}
  1. Lance a exceção diretamente dentro do seu service:
if (existingUser) {
  throw new EmailAlreadyInUseException(dto.email);
}

O HttpExceptionFilter identificará automaticamente a instância de AppException, extraindo seu ErrorCode e status HTTP e montando a resposta no formato RFC 7807 sem necessidade de alterações no filtro global.

On this page