class-validator

⭐ Interview Importance: LOW
⏱️ Revision Time: 7 min

class-validator is the foundational library used by NestJS to define declarative validation rules using TypeScript decorators.

Overview

Instead of writing manual validation logic (e.g., if (!body.email.includes('@')) throw Error()), class-validator allows you to attach rules directly to the properties of your DTO classes.

NestJS’s ValidationPipe reads these rules at runtime and executes them against incoming data.

Key Concepts

  • Declarative Rules: Rules are defined once in the DTO, keeping your controller and service logic completely free of validation clutter.
  • Vast Ecosystem: It provides decorators for almost every common validation scenario (strings, numbers, dates, arrays, enums, UUIDs, credit cards, IP addresses).
  • Customizable Messages: Every decorator accepts an options object where you can define a custom error message.

Code Examples

Comprehensive Validation DTO

This example highlights some of the most useful decorators available in the library.

import { 
  IsString, 
  IsInt, 
  IsEmail, 
  IsOptional, 
  IsEnum, 
  IsUrl, 
  IsArray, 
  MinLength, 
  Max, 
  Matches 
} from 'class-validator';

enum UserRole {
  ADMIN = 'admin',
  USER = 'user',
}

export class CreateProfileDto {
  // Must be a string, at least 4 characters
  @IsString()
  @MinLength(4, { message: 'Username is too short!' }) // Custom message
  username: string;

  // Must be a valid email format
  @IsEmail()
  email: string;

  // Doesn't have to be provided. But if it IS provided, it must be an integer <= 120
  @IsOptional()
  @IsInt()
  @Max(120)
  age?: number;

  // Must match one of the exact strings in the enum
  @IsEnum(UserRole)
  role: UserRole;

  // Must be an array, and every item inside the array must be a valid URL string
  @IsArray()
  @IsUrl({}, { each: true }) // The 'each' flag is critical for arrays of primitives!
  websiteLinks: string[];

  // Custom Regex matching (e.g., a specific zip code format)
  @Matches(/^[0-9]{5}(-[0-9]{4})?$/, { message: 'Invalid Zip Code' })
  zipCode: string;
}

Best Practices

  • @IsOptional() vs Defaults: Do not confuse TypeScript’s optional chaining ? with validation optionality. If a field is not required by your API, you must explicitly use the @IsOptional() decorator. If you only use age?: number; without the decorator, and the user omits the field, other validators (like @IsInt()) will fail because they receive undefined.
  • The each: true flag: When validating arrays of primitives (strings, numbers), always remember to pass { each: true } into the decorator (e.g., @IsString({ each: true })). Otherwise, class-validator will try to validate the array object itself as a single string, which will fail.