Skip to main content

DTO validation

To automatically validate a DTO in NestJS with class-validator, follow 4 steps:

1) Install the packages

javascript
npm i class-validator class-transformer

2) Define the DTO with decorators

javascript
// dto/create-user.dto.ts import { IsEmail, IsString, Length, IsOptional, IsInt, Min } from 'class-validator'; export class CreateUserDto { @IsString() @Length(3, 50) name: string; @IsEmail() email: string; @IsOptional() @IsInt() @Min(0) age?: number; }

For nested objects/arrays, use @ValidateNested() + @Type(() => Class) from class-transformer.

3) Enable a global ValidationPipe (once, in main.ts)

javascript
// main.ts import { ValidationPipe } from '@nestjs/common'; import { NestFactory } from '@nestjs/core'; import { AppModule } from './app.module'; async function bootstrap() { const app = await NestFactory.create(AppModule); app.useGlobalPipes(new ValidationPipe({ transform: true, // string → number/boolean based on the DTO's types whitelist: true, // strips fields not present in the DTO forbidNonWhitelisted: true, // throws a 400 if extra fields are sent stopAtFirstError: false, // collect every error (or true for just the first) transformOptions: { enableImplicitConversion: true }, // simple auto-conversions })); await app.listen(3000); } bootstrap();

The pipe can be applied locally: @Post() create(@Body(new ValidationPipe()) dto: CreateUserDto) { … }, but it's usually more convenient globally.

4) Use the DTO in the controller

javascript
// users.controller.ts import { Body, Controller, Post } from '@nestjs/common'; import { CreateUserDto } from './dto/create-user.dto'; @Controller('users') export class UsersController { @Post() create(@Body() dto: CreateUserDto) { // by this point the object is already VALIDATED and TRANSFORMED return { ok: true, dto }; } }

Useful techniques

  • Nested DTOs

    javascript
    import { ValidateNested, IsArray } from 'class-validator'; import { Type } from 'class-transformer'; class AddressDto { @IsString() city: string; } class CreateUserDto { @ValidateNested() @Type(() => AddressDto) address: AddressDto; @IsArray() @ValidateNested({ each: true }) @Type(() => AddressDto) previousAddresses: AddressDto[]; }
  • Partial updates (PATCH) Use the helpers from @nestjs/mapped-types:

    javascript
    import { PartialType } from '@nestjs/mapped-types'; export class UpdateUserDto extends PartialType(CreateUserDto) {}
  • Custom messages and localization

    javascript
    @Length(3, 50, { message: 'Name must be between 3 and 50 characters' })
  • A custom error shape ValidationPipe accepts an exceptionFactory to return the response shape you want:

    javascript
    new ValidationPipe({ exceptionFactory: (errors) => new BadRequestException({ errors }) })
  • Validation groups (different rules for create/update)

    javascript
    @IsString({ groups: ['create'] }) // pipe: new ValidationPipe({ groups: ['create'] })
  • Optional fields Always add @IsOptional() alongside the validators when a field isn't required.

Summary

  1. A DTO with class-validator decorators.
  2. A global ValidationPipe with transform, whitelist, forbidNonWhitelisted.
  3. Using the DTO in controller signatures.
  4. For nested structures, ValidateNested + @Type.

Short Answer

Interview ready
Premium

A concise answer to help you respond confidently on this topic during an interview.