Validation ensures bad input never reaches your business logic. This lesson covers setting up class-validator, class-transformer, and enabling ValidationPipe globally.
Enabling Global Validation
NestJS ships a built-in ValidationPipe that, combined with class-validator decorators on your DTOs, automatically validates incoming request bodies, query parameters, and route parameters, rejecting invalid requests with a 400 Bad Request before your handler runs.
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({ whitelist: true, transform: true }));
await app.listen(3000);
}
bootstrap();
whitelist: true strips any properties not declared on the DTO, and transform: true converts plain incoming objects into actual instances of the DTO class.
ValidationPipe Options
new ValidationPipe({
whitelist: true, // strip unknown properties
forbidNonWhitelisted: true, // reject requests with unknown properties
transform: true, // convert payloads into DTO instances
transformOptions: { enableImplicitConversion: true },
})
whitelist silently removes properties not defined in the DTO.
forbidNonWhitelisted instead throws an error when unknown properties are present.
transform is required for automatic type conversion of route/query parameters.
You can also apply ValidationPipe per-route instead of globally, with @UsePipes().
Validation Setup Cheatsheet
The pieces required for end-to-end validation in NestJS.
Piece
Role
class-validator
Provides decorators like @IsString(), @IsInt()
class-transformer
Converts plain objects into typed class instances
ValidationPipe
Runs validation and throws 400 on failure
app.useGlobalPipes()
Registers a pipe for every route in the app
@UsePipes()
Registers a pipe for a specific controller or route
Customizing Validation Error Responses
By default, ValidationPipe returns a structured 400 response listing every failed constraint. You can customize this shape with the exceptionFactory option, useful for matching a specific API error format.
new ValidationPipe({
exceptionFactory: (errors) =>
new BadRequestException(
errors.map((e) => ({ field: e.property, issues: Object.values(e.constraints ?? {}) })),
),
})
Validating Query Strings and Route Parameters
ValidationPipe validates any parameter decorated with @Body(), @Query(), or @Param(), as long as its type is a class with class-validator decorators, not just request bodies.
Give query parameters their own DTO class rather than typing them as any.
Enable transform: true so numeric query strings become actual numbers before validation.
Route parameters are usually validated with dedicated pipes (ParseIntPipe) rather than a DTO.
Common Mistakes
Adding class-validator decorators to a DTO but forgetting to enable ValidationPipe anywhere.
Leaving transform: false (the default) and being surprised numeric strings aren't converted.
Not setting whitelist: true, silently allowing clients to send unexpected extra fields.
Returning raw class-validator error objects to clients without shaping them into a consistent API error format.
Key Takeaways
class-validator decorators declare rules directly on DTO properties.
ValidationPipe enforces those rules automatically and rejects invalid requests with 400.
whitelist and transform are the two options worth enabling almost universally.
Validation applies to @Body(), @Query(), and @Param(), not just request bodies.
Pro Tip
Enable ValidationPipe globally in main.ts on day one of a new project, even before you've written your first DTO. It's far easier to have validation ready and waiting than to retrofit it once dozens of unvalidated endpoints already exist.
You now know how to validate requests thoroughly. Next, learn about API Versioning to evolve your API safely over time.