class-validator

repository·develop·Indexed 11 days ago

https://github.com/typestack/class-validator

A library for decorator-based and non-decorator-based property validation for classes in TypeScript and JavaScript. Compatible with Node.js and browser environments, it provides a suite of validation decorators (e.g., @IsInt, @IsEmail), support for nested object validation via @ValidateNested, and the ability to create custom validation constraints and decorators. Version 0.15.1.

Tokens
14.8K
Snippets
38
Records
51
Agent score
92%

What's inside class-validator

  1. Use validation groups for different schemas

    develop

    Validation groups allow you to apply different validation rules to the same class depending on the context (e.g., 'registration' vs 'admin').

    • Assign groups to decorators using the groups option: @Min(12, { groups: ['registration'] }).
    • Specify active groups in the validate method: validate(user, { groups: ['registration'] }).
    • Use the always: true option in a decorator to ensure that validation is applied regardless of which group is active.

    Warning: If you provide a group combination that matches no decorators, it will result in an unknown value error.

    import { validate, Min, Length } from 'class-validator';
    
    export class User {
      @Min(12, { groups: ['registration'] })
      age: number;
    
      @Length(2, 20, { groups: ['registration', 'admin'] })
      name: string;
    }
    
    let user = new User();
    user.age = 10;
    user.name = 'Alex';
    
    // Only validates 'age' (and it will fail because age is 10)
    validate(user, { groups: ['registration'] });
    
    // Only validates 'name' (and it will pass)
    validate(user, { groups: ['admin'] });
  2. How validation decorator inheritance works

    develop

    Subclasses automatically inherit validation decorators from their parent classes.

    If a property is redefined in a subclass, the decorators from both the base class and the subclass will be applied to that property. This allows you to extend validation rules (e.g., adding a @MinLength to a property that was already marked as @IsString in the base class).

    import { IsEmail, IsString, MinLength, MaxLength, Contains } from 'class-validator';
    
    class BaseContent {
      @IsEmail()
      email: string;
    
      @IsString()
      password: string;
    }
    
    class User extends BaseContent {
      @MinLength(10)
      @MaxLength(20)
      name: string;
    
      @Contains('hello')
      welcome: string;
    
      // Redefining password: inherits @IsString and adds @MinLength
      @MinLength(20)
      password: string;
    }
  3. Schema-based validation availability

    develop
    Schema-based validation without decorators is not supported by class-validator. This feature was deprecated in version 0.12 and will not be restored. If your project requires schema-based validation (rather than decorator-based validation on class instances), consider using alternative frameworks like Zod.
  4. How to validate plain JavaScript objects

    develop

    Because class-validator relies on decorators, it requires the object being validated to be an instance of a class (created via new Class()). You cannot validate plain JavaScript objects (such as object literals or objects returned from JSON.parse) directly with class-validator decorators.

    To validate plain objects, you must first transform them into class instances using class-transformer.

  5. Basic usage of class-validator

    develop

    To use class-validator, define a class and apply validation decorators (like @IsInt, @IsEmail, @Min, etc.) to its properties. You can then validate an instance of the class using the validate function or validateOrReject function.

    • validate(object): Returns a Promise that resolves to an array of ValidationError objects. If validation succeeds, the array is empty.
    • validateOrReject(object): Returns a Promise that resolves if validation succeeds, or rejects with an array of ValidationError objects if validation fails.
    import {
      validate,
      validateOrReject,
      Contains,
      IsInt,
      Length,
      IsEmail,
      IsFQDN,
      IsDate,
      Min,
      Max,
    } from 'class-validator';
    
    export class Post {
      @Length(10, 20)
      title: string;
    
      @Contains('hello')
      text: string;
    
      @IsInt()
      @Min(0)
      @Max(10)
      rating: number;
    
      @IsEmail()
      email: string;
    
      @IsFQDN()
      site: string;
    
      @IsDate()
      createDate: Date;
    }
    
    let post = new Post();
    post.title = 'Hello'; // should not pass
    
    // Using validate()
    validate(post).then(errors => {
      if (errors.length > 0) {
        console.log('validation failed. errors: ', errors);
      } else {
        console.log('validation succeed');
      }
    });
    
    // Using validateOrReject()
    async function validateOrRejectExample(input) {
      try {
        await validateOrReject(input);
      } catch (errors) {
        console.log('Caught promise rejection (validation failed). Errors: ', errors);
      }
    }
  6. Integrate a service container for dependency injection

    develop

    class-validator supports service containers, allowing you to inject dependencies into your custom ValidatorConstraint classes.

    To use a container (e.g., typedi), call useContainer(Container) at the global application level. Once configured, you can inject the Validator class from the container, and your custom constraints can use constructor injection to access services.

    import { Container } from 'typedi';
    import { useContainer, Validator } from 'class-validator';
    
    // Global setup
    useContainer(Container);
    let validator = Container.get(Validator);
    
    // Custom ValidatorConstraints can now use constructor injection
  7. Validate promises with @ValidatePromise()

    develop

    If a property contains a Promise that resolves to a value that needs validation, use the @ValidatePromise() decorator. This can be combined with @ValidateNested() if the promise resolves to a class instance.

    import { ValidatePromise, Min, ValidateNested } from 'class-validator';
    
    export class Post {
      @Min(0)
      @ValidatePromise()
      userId: Promise<number>;
    
      @ValidateNested()
      @ValidatePromise()
      user: Promise<User>;
    }
  8. Create custom validation constraints

    develop

    To implement custom validation logic, create a class that implements the ValidatorConstraintInterface and decorate it with @ValidatorConstraint.

    1. Define the Constraint: Implement validate(value, args) (can be async) and optionally defaultMessage(args).
    2. Use the Constraint: Apply it to a property using the @Validate(ConstraintClass, [args], options) decorator.
    3. Access Arguments: Inside the validate method, you can access passed constraints via validationArguments.constraints.

    If you provide a name to @ValidatorConstraint({ name: '...' }), that name will be used as the error type in the ValidationError object.

    import { 
      ValidatorConstraint, 
      ValidatorConstraintInterface, 
      ValidationArguments, 
      Validate 
    } from 'class-validator';
    
    @ValidatorConstraint({ name: 'customText', async: false })
    export class CustomTextLength implements ValidatorConstraintInterface {
      validate(text: string, args: ValidationArguments) {
        // Access constraints passed via @Validate(CustomTextLength, [min, max])
        const min = args.constraints[0] || 0;
        const max = args.constraints[1] || 100;
        return text.length > min && text.length < max;
      }
    
      defaultMessage(args: ValidationArguments) {
        return 'Text is too short or too long!';
      }
    }
    
    export class Post {
      @Validate(CustomTextLength, [3, 20], { message: 'Wrong post title' })
      title: string;
    }
  9. Validate nested objects with @ValidateNested()

    develop

    To trigger validation on nested objects, use the @ValidateNested() decorator.

    Important: The nested property must be an instance of a class. If it is a plain object, @ValidateNested() will not know which class schema to use for validation.

    import { ValidateNested } from 'class-validator';
    
    export class User {
      @IsEmail()
      email: string;
    }
    
    export class Post {
      @ValidateNested()
      user: User;
    
      // Also works with multi-dimensional arrays
      @ValidateNested()
      matrix: Point[][];
    }
  10. Customize validation messages and use special tokens

    develop

    You can provide custom error messages directly in the decorator options. You can also provide a function that receives ValidationArguments for more complex logic.

    Special Tokens in Messages:

    • $value: The value being validated.
    • $property: The name of the property being validated.
    • $target: The name of the class being validated.
    • $constraint1, $constraint2, ... $constraintN: The specific constraints defined by the validator (e.g., the minimum length value).

    ValidationArguments properties:

    • value: The value being validated.
    • constraints: Array of constraints defined by the specific validation type.
    • targetName: Name of the object's class.
    • object: The object being validated.
    • property: The name of the property being validated.
    import { MinLength, MaxLength, ValidationArguments } from 'class-validator';
    
    export class Post {
      @MinLength(10, {
        // Using tokens
        message: 'Title is too short. Minimal length is $constraint1 characters, but actual is $value',
      })
      @MaxLength(50, {
        // Using a function for granular messages
        message: (args: ValidationArguments) => {
          if (args.value.length === 1) {
            return 'Too short, minimum length is 1 character';
          } else {
            return 'Too short, minimum length is ' + args.constraints[0] + ' characters';
          }
        },
      })
      title: string;
    }