No desenvolvimento de APIs com NestJS, é fundamental garantir que os dados recebidos nas requisições sejam validados e convertidos para os tipos corretos antes de serem utilizados na lógica da aplicação. Para isso, o NestJS oferece uma poderosa combinação entre DTOs (Data Transfer Objects), o pacote class-validator e o class-transformer, permitindo não apenas a validação, mas também a transformação automática dos dados.
Este artigo aborda como validar e transformar diferentes tipos de dados — como string, boolean, data, email, UUID e outros — usando DTOs no NestJS, além de como configurar as ferramentas necessárias para obter resultados mais consistentes e seguros.
O que é um DTO no NestJS
Um DTO (Data Transfer Object) é uma classe responsável por definir a estrutura esperada dos dados em uma requisição HTTP. Ele é amplamente utilizado nos controladores para receber payloads e garantir que os dados estejam corretos antes de serem passados para os serviços.
Exemplo:
export class CreateUserDto {
name: string;
email: string;
}Ao adicionar decoradores do class-validator, podemos validar os campos. Ao ativar a opção de transformação, o NestJS converte automaticamente os valores recebidos para os tipos desejados.
Como Validar Diferentes Tipos de Dados em DTOs
O pacote class-validator permite validar diversos tipos de dados diretamente dentro dos DTOs. Abaixo estão alguns exemplos práticos.
Campo Tipo String
Garante que o valor seja uma string e respeite limites mínimos e máximos de caracteres:
import { IsString, MinLength, MaxLength } from 'class-validator';
@IsString({ message: 'O nome deve ser uma string.' })
@MinLength(3, { message: 'O nome deve ter pelo menos 3 caracteres.' })
@MaxLength(50, { message: 'O nome não pode exceder 50 caracteres.' })
name: string;Campo Tipo Boolean
Valida que o campo tenha um valor booleano (true ou false):
import { IsBoolean } from 'class-validator';
@IsBoolean({ message: 'O status deve ser verdadeiro ou falso.' })
active: boolean;Campo Tipo Data
Valide datas no formato ISO e transforme-as automaticamente para o tipo Date:
import { IsDateString } from 'class-validator';
@IsDateString(null, { message: 'A data deve estar no formato ISO (ex: YYYY-MM-DD).' })
date: Date;Com a transformação ativada, mesmo que a entrada seja uma string, ela será automaticamente convertida para um objeto Date.
Campo Tipo Email
Verifica se o valor é um endereço de e-mail válido:
import { IsEmail } from 'class-validator';
@IsEmail({}, { message: 'O e-mail informado é inválido.' })
email: string;Campo Tipo UUID
Confirma que o valor é um identificador único universal (UUID), podendo especificar a versão:
import { IsUUID } from 'class-validator';
@IsUUID('4', { message: 'O ID deve ser um UUID versão 4 válido.' })
id: string;Campo do tipo Array
import {
IsArray,
ArrayMinSize,
ValidateNested
} from 'class-validator';
import { Type } from 'class-transformer';
import { ProductDto } from './product.dto';
export class CreateOrderDto {
@IsArray()
@ArrayMinSize(1, { message: 'Pelo menos 1 produto é obrigatório.' })
@ValidateNested({ each: true })
@Type(() => ProductDto)
products: ProductDto[];
}Confirma que a array possui objetos com valores esperado, podendo verificar também o tamanho da array:
Outros Tipos Comuns
- Número:
@IsNumber() - URL:
@IsUrl() - CPF / CNPJ: Use expressões regulares com
@Matches() - Telefone: Também pode ser validado com
@Matches() - Arquivo ou Base64:
@IsBase64()
Como Ativar a Transformação Automática de Dados no NestJS
A transformação automática de dados permite que o NestJS converta automaticamente os valores recebidos no corpo das requisições para os tipos especificados nos DTOs. Por exemplo, uma string pode ser convertida para número ou data.
Para ativar essa funcionalidade, instale as dependências necessárias:
npm install class-validator class-transformerEm seguida, configure o ValidationPipe no arquivo main.ts:
// src/main.ts
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { ValidationPipe } from '@nestjs/common';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.useGlobalPipes(
new ValidationPipe({
whitelist: true,
forbidNonWhitelisted: true,
transform: true, // Habilita a transformação automática
transformOptions: {
enableImplicitConversion: true, // Permite conversão implícita de tipos
},
validationError: {
target: false,
value: true,
},
}),
);
await app.listen(3000);
}
bootstrap();Com essa configuração:
- Campos declarados como
Dateserão automaticamente convertidos. - Valores numéricos recebidos como strings são convertidos para números.
- Campos booleanos também são convertidos corretamente (por exemplo,
'true'→true).
Uso do @Type() para Transformação Explícita
Em alguns casos, especialmente ao lidar com objetos aninhados ou listas de objetos, o NestJS não consegue inferir automaticamente o tipo esperado. Nesses cenários, devemos usar o decorator @Type() do pacote class-transformer.
Exemplo 1: Objeto Aninhado
// address.dto.ts
export class AddressDto {
street: string;
city: string;
}// user.dto.ts
import { Type } from 'class-transformer';
import { AddressDto } from './address.dto';
export class UserDto {
name: string;
@Type(() => AddressDto)
address: AddressDto;
}Exemplo 2: Lista de Objetos
export class UserDto {
name: string;
@Type(() => AddressDto)
addresses: AddressDto[];
}Exemplo 3: Converter String para Date
import { Type } from 'class-transformer';
export class EventDto {
@Type(() => Date)
date: Date;
}Esse recurso garante que os dados sejam corretamente instanciados como objetos das classes especificadas, aumentando a clareza e robustez da sua aplicação.
Personalizando Mensagens de Erro em Português
Você pode personalizar as mensagens de erro lançadas durante a validação dos DTOs adicionando a propriedade message nos decoradores:
@IsEmail({}, { message: 'E-mail inválido. Exemplo: [email protected]' })
email: string;Também é possível criar pipes personalizados para tratar erros de forma mais granular:
throw new BadRequestException('Formato de data inválido.');Além disso, você pode centralizar todas as mensagens de erro em um serviço ou arquivo JSON para facilitar futuras adaptações ou internacionalização.
Como Usar o DTO no Controller
Os DTOs são utilizados diretamente nos métodos dos controladores via o decorator @Body().
// src/controllers/user.controller.ts
import { Controller, Post, Body } from '@nestjs/common';
import { CreateUserDto } from '../dto/create-user.dto';
@Controller('users')
export class UserController {
@Post()
create(@Body() createUserDto: CreateUserDto) {
return {
message: 'Usuário criado com sucesso',
data: createUserDto,
};
}
}Se os dados recebidos não forem compatíveis com o DTO, o método create() não será executado. Em vez disso, o NestJS lança automaticamente um erro 400 com detalhes sobre o problema.
Fluxo do DTO no NestJS
O fluxo de execução no NestJS ao usar DTOs é o seguinte:
- A requisição HTTP chega ao controller.
- O NestJS tenta mapear o corpo da requisição para o tipo definido no DTO.
- Se o
ValidationPipeestiver ativado:
- As validações são executadas.
- Caso haja falhas, o NestJS interrompe a execução e retorna uma resposta de erro imediatamente.
- Caso contrário, o DTO é populado e repassado ao método do controlador.
Dessa forma, qualquer erro na validação do DTO impede que o método do controlador seja executado, evitando chamadas desnecessárias e mantendo a consistência da API.
Conclusão
Implementar DTOs com validação e transformação de dados no NestJS é uma prática essencial para construir APIs robustas, organizadas e seguras. Através do uso combinado de class-validator e class-transformer, é possível validar e converter automaticamente os tipos de dados recebidos nas requisições, garantindo maior consistência e reduzindo erros na camada de negócios.
Ao seguir essas boas práticas, incluindo o uso do @Type() para transformação explícita, você melhora a qualidade do código, aumenta a clareza dos dados e facilita a manutenção e escalabilidade da sua aplicação.
One comment