DTO validation
To automatically validate a DTO in NestJS with class-validator, follow 4 steps:
1) Install the packages
npm i class-validator class-transformer2) Define the DTO with decorators
// 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)fromclass-transformer.
3) Enable a global ValidationPipe (once, in main.ts)
// 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
// 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
javascriptimport { 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:javascriptimport { 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
ValidationPipeaccepts anexceptionFactoryto return the response shape you want:javascriptnew 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
- A DTO with
class-validatordecorators. - A global
ValidationPipewithtransform,whitelist,forbidNonWhitelisted. - Using the DTO in controller signatures.
- For nested structures,
ValidateNested+@Type.
Short Answer
Interview readyA concise answer to help you respond confidently on this topic during an interview.