Validate DTOs Globally With One Line
The Built-In ValidationPipe
ValidationPipe plus class-validator gives you typed, validated request bodies automatically — usually all you need to harden an API surface.
What you'll learn
- Install class-validator and class-transformer
- Enable ValidationPipe globally with whitelist and transform
- Understand each option and why it matters
ValidationPipe is the single line of code that takes a Nest API from
“trusting strings” to “validated, typed objects.” It uses two libraries to
do its job — install them first.
Setup
npm install class-validator class-transformer Then enable the pipe globally in main.ts:
import { ValidationPipe } from '@nestjs/common';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.useGlobalPipes(
new ValidationPipe({
whitelist: true,
forbidNonWhitelisted: true,
transform: true,
}),
);
await app.listen(3000);
} Every @Body(), @Query(), and @Param() with a DTO class as its type is
now validated and transformed.
What Each Option Does
whitelist: true — strips properties that aren’t declared on the DTO.
A client sending { email, password, isAdmin: true } to a DTO that only
declares email and password gets isAdmin silently dropped. This is
your front-line defense against mass-assignment bugs.
forbidNonWhitelisted: true — escalates from “strip” to “reject.”
The same request now fails with a 400. Stricter, often what you want for
internal APIs.
transform: true — turns plain objects into instances of the DTO
class, and converts primitive types (e.g. "true" from a query string to a
real boolean) according to the field types.
class ListUsersDto {
@IsInt() @Min(1) page: number;
@IsBoolean() active: boolean;
}
// Without transform: page is the string "2", active is the string "true"
// With transform: page is 2, active is true A Validated Endpoint
Define the DTO with class-validator decorators, then use it as a @Body
type. The pipe does the rest.
import { IsEmail, IsString, MinLength } from 'class-validator';
export class CreateUserDto {
@IsEmail()
email: string;
@IsString()
@MinLength(8)
password: string;
}
@Controller('users')
export class UsersController {
@Post()
create(@Body() dto: CreateUserDto) {
// dto is a CreateUserDto instance, already validated
return this.users.create(dto);
}
} A bad request gets a 400 with details:
{
"statusCode": 400,
"message": [
"email must be an email",
"password must be longer than or equal to 8 characters"
],
"error": "Bad Request"
} Local Use
You can also apply ValidationPipe to a single parameter or method — handy
when you want different options per endpoint (e.g. transform: false for
a legacy route).
@Post()
create(@Body(new ValidationPipe({ groups: ['create'] })) dto: CreateUserDto) {
return this.users.create(dto);
} The next lesson zooms into the decorators themselves — @IsString,
@IsEmail, nested validation, and friends.