NestJS Class Validator

Class Validator is an npm package that adds validation rules to DTO properties using decorators. You place these decorators on each field in your DTO, and the NestJS ValidationPipe reads them at runtime to check whether the incoming data complies. Together, they form the complete validation system for NestJS APIs.

Installing Class Validator

npm install class-validator class-transformer

Both packages work together. class-validator provides the validation decorators. class-transformer handles the object transformation that enables those decorators to run.

Adding Validation to a DTO

import { IsString, IsEmail, IsInt, Min, Max, IsOptional, IsNotEmpty } from 'class-validator';

export class CreateUserDto {
  @IsNotEmpty()
  @IsString()
  name: string;

  @IsEmail()
  email: string;

  @IsInt()
  @Min(1)
  @Max(120)
  age: number;

  @IsOptional()
  @IsString()
  role?: string;
}

Each decorator adds one rule. Multiple decorators on the same field all run, and all must pass. If age is 150, both @Min(1) and @Max(120) fail and the error response lists both failures.

Common Validation Decorators

String Validators

@IsString()           ← must be a string
@IsNotEmpty()         ← string must not be empty ('')
@MinLength(3)         ← at least 3 characters
@MaxLength(100)       ← at most 100 characters
@Matches(/^[a-z]+$/)  ← must match the regex pattern
@IsEmail()            ← valid email format
@IsUrl()              ← valid URL format
@IsUUID()             ← valid UUID (e.g., 'abc123-...')

Number Validators

@IsInt()              ← must be an integer
@IsNumber()           ← any number (including decimals)
@Min(0)               ← not less than 0
@Max(100)             ← not more than 100
@IsPositive()         ← must be greater than 0
@IsNegative()         ← must be less than 0

Boolean Validators

@IsBoolean()          ← must be true or false

Date Validators

@IsDate()             ← must be a Date object
@IsDateString()       ← valid ISO date string ('2025-01-15')

Array Validators

@IsArray()            ← must be an array
@ArrayMinSize(1)      ← array must have at least 1 element
@ArrayMaxSize(10)     ← array must have at most 10 elements

Presence Validators

@IsOptional()         ← field may be absent; skip validation if missing
@IsDefined()          ← field must not be null or undefined
@IsNotEmpty()         ← value must not be null, undefined, or ''

Nested Object Validation

When a DTO contains a nested object, decorate the field with @ValidateNested() and @Type() to validate the nested object's fields too:

import { ValidateNested } from 'class-validator';
import { Type } from 'class-transformer';

export class AddressDto {
  @IsString()
  street: string;

  @IsString()
  city: string;
}

export class CreateUserDto {
  @IsString()
  name: string;

  @ValidateNested()
  @Type(() => AddressDto)
  address: AddressDto;
}

Without @ValidateNested(), class-validator treats the nested object as a black box and skips its internal fields entirely.

Array of Objects Validation

export class CreateOrderDto {
  @IsArray()
  @ValidateNested({ each: true })
  @Type(() => OrderItemDto)
  items: OrderItemDto[];
}

The { each: true } option tells class-validator to run validation on every element in the array, not just the array itself.

Custom Error Messages

Every decorator accepts an options object as the last argument where you can set a custom error message:

@IsEmail({}, { message: 'email must be a valid email address' })
email: string;

@Min(18, { message: 'You must be at least 18 years old' })
age: number;

Custom Validators

For rules that built-in decorators cannot handle, create a custom validator using ValidatorConstraint:

import { ValidatorConstraint, ValidatorConstraintInterface, registerDecorator } from 'class-validator';

@ValidatorConstraint({ async: false })
export class IsUsernameAllowedConstraint implements ValidatorConstraintInterface {
  validate(username: string) {
    const blockedNames = ['admin', 'root', 'system'];
    return !blockedNames.includes(username.toLowerCase());
  }

  defaultMessage() {
    return 'This username is reserved';
  }
}

// Apply it as a decorator
export function IsUsernameAllowed() {
  return function (object: object, propertyName: string) {
    registerDecorator({
      target: object.constructor,
      propertyName,
      validator: IsUsernameAllowedConstraint,
    });
  };
}

Validation Flow With Class Validator

POST /users  body: { name: '', email: 'bad', age: 200 }

      |
      v
ValidationPipe reads body
      |
      v
class-validator runs all decorators on CreateUserDto
  @IsNotEmpty() on name      → FAIL (empty string)
  @IsEmail() on email        → FAIL (not an email)
  @Max(120) on age           → FAIL (200 > 120)
      |
      v
Three errors collected
      |
      v
400 Bad Request: ["name should not be empty", "email must be an email", "age must not be greater than 120"]

Class Validator makes your DTOs self-documenting. Reading a DTO class tells you exactly what the API expects: types, formats, minimum values, maximum lengths, and more — all expressed as clear, readable decorators sitting directly on each field.

Leave a Comment

Your email address will not be published. Required fields are marked *