You've met the guards (class-validator decorators) and you know how to describe validation rules in DTOs. But who actually activates these guards? The answer is ValidationPipe — it's like the chief control officer standing at the gate and coordinating the work of all the guards.
ValidationPipe is a built-in NestJS pipe that automatically:
The most convenient way is to enable ValidationPipe globally for the entire application:
1// main.ts - The Empire's Main Gate
2import { NestFactory } from '@nestjs/core';
3import { ValidationPipe } from '@nestjs/common';
4import { AppModule } from './app.module';
5
6async function bootstrap() {
7 const app = await NestFactory.create(AppModule);
8
9 // Setting up global gate guard
10 app.useGlobalPipes(new ValidationPipe());
11
12 await app.listen(3000);
13}
14bootstrap();ValidationPipe accepts an options object that allows you to customize guard behavior:
1app.useGlobalPipes(new ValidationPipe({
2 whitelist: true,
3}));With the
whitelist: true option, the pipe automatically removes all fields that don't have validation decorators in the DTO. It's like a guard confiscating suspicious items at the gate:1// DTO only allows 'name' and 'age'
2export class CreateLegionaryDto {
3 @IsString()
4 name: string;
5
6 @IsNumber()
7 age: number;
8}
9
10// A request comes in with an extra field:
11// { name: "Marcus", age: 25, isAdmin: true }
12
13// With whitelist: true, the 'isAdmin' field is removed
14// Controller receives: { name: "Marcus", age: 25 }1app.useGlobalPipes(new ValidationPipe({
2 whitelist: true,
3 forbidNonWhitelisted: true,
4}));This is an even stricter option — instead of removing unknown fields, the server returns an error. The guard doesn't confiscate items but immediately denies entry:
1// { name: "Marcus", age: 25, isAdmin: true }
2// Response: 400 Bad Request
3// "property isAdmin should not exist"1app.useGlobalPipes(new ValidationPipe({
2 transform: true,
3}));URL parameters always arrive as strings. With the
transform: true option, the pipe automatically converts them to the appropriate types:1@Get(':id')
2findOne(@Param('id') id: number) {
3 // Without transform: id is a string "42"
4 // With transform: id is a number 42
5 console.log(typeof id); // "number"
6}You can also use ValidationPipe on a specific endpoint instead of globally:
1@Post()
2@UsePipes(new ValidationPipe({ whitelist: true }))
3createLegionary(@Body() dto: CreateLegionaryDto) {
4 return this.service.create(dto);
5}Or on a specific parameter:
1@Post()
2createLegionary(
3 @Body(new ValidationPipe({ whitelist: true })) dto: CreateLegionaryDto,
4) {
5 return this.service.create(dto);
6}Here is the configuration used by most NestJS projects:
1app.useGlobalPipes(new ValidationPipe({
2 whitelist: true,
3 forbidNonWhitelisted: true,
4 transform: true,
5 transformOptions: {
6 enableImplicitConversion: true,
7 },
8}));This configuration ensures:
whitelist)forbidNonWhitelisted)transform)enableImplicitConversion)ValidationPipe is the foundation of your API's security. Without it, class-validator decorators are like guards without a commander — they don't know when to act!