Skip to content

NestJS Validation

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.