ValidationPipe
The ValidationPipe is NestJS’s built-in, immensely powerful pipe designed specifically to work seamlessly with the class-validator and class-transformer libraries.
Overview
While you can write custom validation pipes for every route, it’s incredibly tedious. The ValidationPipe provides a declarative approach.
By applying this pipe globally, you can simply decorate your Data Transfer Object (DTO) classes with validation rules (like @IsString(), @IsEmail()). When a request comes in, the ValidationPipe intercepts the body, checks the DTO class definition, runs the validation rules, and either passes the data to your controller or automatically throws a 400 Bad Request if the data is invalid.
Key Concepts
- Integration: It bridges the gap between NestJS and the
class-validatorecosystem. - DTOs: Data Transfer Objects are the core of this system. They are simple TypeScript classes defining the expected shape of the data.
- Auto-error formatting: It automatically aggregates all validation errors and formats them into a clean, readable JSON array for the client.
Code Examples
Setting up the Global Validation Pipe
The most common way to use ValidationPipe is to apply it globally in your main.ts file.
import { NestFactory } from '@nestjs/core';
import { ValidationPipe } from '@nestjs/common';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
// Apply it globally!
app.useGlobalPipes(new ValidationPipe({
// Highly recommended options:
whitelist: true, // Strips away any properties that don't have decorators
forbidNonWhitelisted: true, // Throws an error if the user sends unknown properties
transform: true, // Automatically transforms plain JSON objects into instances of your DTO class
}));
await app.listen(3000);
}
bootstrap();
The DTO and Controller
Once the pipe is set up globally, you just define your DTO and use it in your controller.
// create-user.dto.ts
import { IsString, IsEmail, MinLength } from 'class-validator';
export class CreateUserDto {
@IsString()
@MinLength(3)
username: string;
@IsEmail()
email: string;
}
// users.controller.ts
import { Controller, Post, Body } from '@nestjs/common';
import { CreateUserDto } from './create-user.dto';
@Controller('users')
export class UsersController {
@Post()
create(@Body() createUserDto: CreateUserDto) {
// 1. The ValidationPipe sees that @Body() is typed as CreateUserDto.
// 2. It intercepts the incoming JSON request body.
// 3. It checks the body against the decorators in CreateUserDto.
// 4. If it fails, it throws a 400 error.
// 5. If it passes, execution reaches here!
return 'User created successfully';
}
}
Best Practices
- Always use
whitelist: true: This is a critical security feature. If yourUserDtoonly definesnameandemail, but a malicious user sends{ "name": "A", "email": "B", "isAdmin": true },whitelist: truewill silently strip outisAdminbefore it reaches your controller, preventing mass-assignment vulnerabilities. - Always use
transform: true: This ensures that numeric strings in query parameters (like?page=5) are automatically converted to actual numbers if your DTO definespage: number.