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.
