The Built-In ValidationPipe

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.

4 min read Level 2/5 #nestjs#pipes#validation
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.

class-validator — DTOs With Rules →