class-validator
Allows use of decorator and non-decorator based validation. Internally uses [validator.js][1] to perform validation. Class-validator works on both browser and node.js platforms.
Table of Contents
- class-validator
- Table of Contents
- Installation
- Usage
- Validation errors
- Validation messages
- Validating arrays
- Validating sets
- Validating maps
- Validating nested objects
- Validating promises
- Inheriting Validation decorators
- Conditional validation
- Whitelisting
- Passing context to decorators
- Skipping missing properties
- Validation groups
- Custom validation classes
- Custom validation decorators
- Using service container
- Synchronous validation
- Manual validation
- Validation decorators
- Defining validation schema without decorators
- Validating plain objects
- Samples
- Extensions
- Release notes
- Contributing
Installation
npm install class-validator --save
Note: Please use at least npm@6 when using class-validator. From npm@6 the dependency tree is flattened, which is required by
class-validator
to function properly.
Usage
Create your class and put some validation decorators on the properties you want to validate:
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
post.text = 'this is a great post about hell world'; // should not pass
post.rating = 11; // should not pass
post.email = 'google.com'; // should not pass
post.site = 'googlecom'; // should not pass
validate(post).then(errors => {
// errors is an array of validation errors
if (errors.length > 0) {
console.log('validation failed. errors: ', errors);
} else {
console.log('validation succeed');
}
});
validateOrReject(post).catch(errors => {
console.log('Promise rejected (validation failed). Errors: ', errors);
});
// or
async function validateOrRejectExample(input) {
try {
await validateOrReject(input);
} catch (errors) {
console.log('Caught promise rejection (validation failed). Errors: ', errors);
}
}
Passing options
The validate
function optionally expects a ValidatorOptions
object as a second parameter:
export interface ValidatorOptions {
skipMissingProperties?: boolean;
whitelist?: boolean;
forbidNonWhitelisted?: boolean;
groups?: string[];
dismissDefaultMessages?: boolean;
validationError?: {
target?: boolean;
value?: boolean;
};
forbidUnknownValues?: boolean;
stopAtFirstError?: boolean;
}
IMPORTANT The
forbidUnknownValues
value is set totrue
by default and it is highly advised to keep the default. Setting it tofalse
will result unknown objects passing the validation!
Validation errors
The validate
method returns an array of ValidationError
objects. Each ValidationError
is:
{
target: Object; // Object that was validated.
property: string; // Object's property that haven't pass validation.
value: any; // Value that haven't pass a validation.
constraints?: { // Constraints that failed validation with error messages.
[type: string]: string;
};
children?: ValidationError[]; // Contains all nested validation errors of the property
}
In our case, when we validated a Post object, we have such an array of ValidationError
objects:
[{
target: /* post object */,
property: "title",
value: "Hello",
constraints: {
length: "$property must be longer than or equal to 10 characters"
}
}, {
target: /* post object */,
property: "text",
value: "this is a great post about hell world",
constraints: {
contains: "text must contain a hello string"
}
},
// and other errors
]
If you don't want a target
to be exposed in validation errors, there is a special option when you use validator:
validator.validate(post, { validationError: { target: false } });
This is especially useful when you send errors back over http, and you most probably don't want to expose the whole target object.
Validation messages
You can specify validation message in the decorator options and that message will be returned in the ValidationError
returned by the validate
method (in the case that validation for this field fails).
import { MinLength, MaxLength } from 'class-validator';
export class Post {
@MinLength(10, {
message: 'Title is too short',
})
@MaxLength(50, {
message: 'Title is too long',
})
title: string;
}
There are few special tokens you can use in your messages:
$value
- the value that is being validated$property
- name of the object's property being validated$target
- name of the object's class being validated$constraint1
,$constraint2
, ...$constraintN
- constraints defined by specific validation type
Example of usage:
import { MinLength, MaxLength } from 'class-validator';
export class Post {
@MinLength(10, {
// here, $constraint1 will be replaced with "10", and $value with actual supplied value
message: 'Title is too short. Minimal length is $constraint1 characters, but actual is $value',
})
@MaxLength(50, {
// here, $constraint1 will be replaced with "50", and $value with actual supplied value
message: 'Title is too long. Maximal length is $constraint1 characters, but actual is $value',
})
title: string;
}
Also you can provide a function, that returns a message. This allows you to create more granular messages:
import { MinLength, MaxLength, ValidationArguments } from 'class-validator';
export class Post {
@MinLength(10, {
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;
}
Message function accepts ValidationArguments
which contains the following information:
value
- the value that is being validatedconstraints
- array of constraints defined by specific validation typetargetName
- name of the object's class being validatedobject
- object that is being validatedproperty
- name of the object's property being validated
Validating arrays
If your field is an array and you want to perform validation of each item in the array you must specify a
special each: true
decorator option:
import { MinLength, MaxLength } from 'class-validator';
export class Post {
@MaxLength(20, {
each: true,
})
tags: string[];
}
This will validate each item in post.tags
array.
Validating sets
If your field is a set and you want to perform validation of each item in the set you must specify a
special each: true
decorator option:
import { MinLength, MaxLength } from 'class-validator';
export class Post {
@MaxLength(20, {
each: true,
})
tags: Set<string>;
}
This will validate each item in post.tags
set.
Validating maps
If your field is a map and you want to perform validation of each item in the map you must specify a
special each: true
decorator option:
import { MinLength, MaxLength } from 'class-validator';
export class Post {
@MaxLength(20, {
each: true,
})
tags: Map<string, string>;
}
This will validate each item in post.tags
map.
Validating nested objects
If your object contains nested objects and you want the validator to perform their validation too, then you need to
use the @ValidateNested()
decorator:
import { ValidateNested } from 'class-validator';
export class Post {
@ValidateNested()
user: User;
}
Please note that nested object must be an instance of a class, otherwise @ValidateNested
won't know what class is target of validation. Check also Validating plain objects.
It also works with multi-dimensional array, like :
import { ValidateNested } from 'class-validator';
export class Plan2D {
@ValidateNested()
matrix: Point[][];
}
Validating promises
If your object contains property with Promise
-returned value that should be validated, then you need to use the @ValidatePromise()
decorator:
import { ValidatePromise, Min } from 'class-validator';
export class Post {
@Min(0)
@ValidatePromise()
userId: Promise<number>;
}
It also works great with @ValidateNested
decorator:
import { ValidateNested, ValidatePromise } from 'class-validator';
export class Post {
@ValidateNested()
@ValidatePromise()
user: Promise<User>;
}
Inheriting Validation decorators
When you define a subclass which extends from another one, the subclass will automatically inherit the parent's decorators. If a property is redefined in the descendant, class decorators will be applied on it from both its own class and the base class.
import { validate } 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;
@MinLength(20)
password: string;
}
let user = new User();
user.email = 'invalid email'; // inherited property
user.password = 'too short'; // password wil be validated not only against IsString, but against MinLength as well
user.name = 'not valid';
user.welcome = 'helo';
validate(user).then(errors => {
// ...
}); // it will return errors for email, password, name and welcome properties
Conditional validation
The conditional validation decorator (@ValidateIf
) can be used to ignore the validators on a property when the provided condition function returns false. The condition function takes the object being validated and must return a boolean
.
import { ValidateIf, IsNotEmpty } from 'class-validator';
export class Post {
otherProperty: string;
@ValidateIf(o => o.otherProperty === 'value')
@IsNotEmpty()
example: string;
}
In the example above, the validation rules applied to example
won't be run unless the object's otherProperty
is "value"
.
Note that when the condition is false all validation decorators are ignored, including isDefined
.
Whitelisting
Even if your object is an instance of a validation class it can contain additional properties that are not defined.
If you do not want to have such properties on your object, pass special flag to validate
method:
import { validate } from 'class-validator';
// ...
validate(post, { whitelist: true });
This will strip all properties that don't have any decorators. If no other decorator is suitable for your property, you can use @Allow decorator:
import {validate, Allow, Min} from "class-validator";
export class Post {
@Allow()
title: string;
@Min(0)
views: number;
nonWhitelistedProperty: number;
}
let post = new Post();
post.title = 'Hello world!';
post.views = 420;
post.nonWhitelistedProperty = 69;
(post as any).anotherNonWhitelistedProperty = "something";
validate(post).then(errors => {
// post.nonWhitelistedProperty is not defined
// (post as any).anotherNonWhitelistedProperty is not defined
...
});
If you would rather to have an error thrown when any non-whitelisted properties are present, pass another flag to
validate
method:
import { validate } from 'class-validator';
// ...
validate(post, { whitelist: true, forbidNonWhitelisted: true });
Passing context to decorators
It's possible to pass a custom object to decorators which will be accessible on the ValidationError
instance of the property if validation failed.
import { validate } from 'class-validator';
class MyClass {
@MinLength(32, {
message: 'EIC code must be at least 32 characters',
context: {
errorCode: 1003,
developerNote: 'The validated string must contain 32 or more characters.',
},
})
eicCode: string;
}
const model = new MyClass();
validate(model).then(errors => {
//errors[0].contexts['minLength'].errorCode === 1003
});
Skipping missing properties
Sometimes you may want to skip validation of the properties that do not exist in the validating object. This is
usually desirable when you want to update some parts of the object, and want to validate only updated parts,
but skip everything else, e.g. skip missing properties.
In such situations you will need to pass a special flag to validate
method:
import { validate } from 'class-validator';
// ...
validate(post, { skipMissingProperties: true });
When skipping missing properties, sometimes you want not to skip all missing properties, some of them maybe required
for you, even if skipMissingProperties is set to true. For such cases you should use @IsDefined()
decorator.
@IsDefined()
is the only decorator that ignores skipMissingProperties
option.
Validation groups
In different situations you may want to use different validation schemas of the same object. In such cases you can use validation groups.
IMPORTANT Calling a validation with a group combination that would not result in a validation (eg: non existent group name) will result in a unknown value error. When validating with groups the provided group combination should match at least one decorator.
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';
validate(user, {
groups: ['registration'],
}); // this will not pass validation
validate(user, {
groups: ['admin'],
}); // this will pass validation
validate(user, {
groups: ['registration', 'admin'],
}); // this will not pass validation
validate(user, {
groups: undefined, // the default
}); // this will not pass validation since all properties get validated regardless of their groups
validate(user, {
groups: [],
}); // this will not pass validation, (equivalent to 'groups: undefined', see above)
There is also a special flag always: true
in validation options that you can use. This flag says that this validation
must be applied always no matter which group is used.
Custom validation classes
If you have custom validation logic you can create a Constraint class:
-
First create a file, lets say
CustomTextLength.ts
, and define a new class:import { ValidatorConstraint, ValidatorConstraintInterface, ValidationArguments } from 'class-validator'; @ValidatorConstraint({ name: 'customText', async: false }) export class CustomTextLength implements ValidatorConstraintInterface { validate(text: string, args: ValidationArguments) { return text.length > 1 && text.length < 10; // for async validations you must return a Promise<boolean> here } defaultMessage(args: ValidationArguments) { // here you can provide default error message if validation failed return 'Text ($value) is too short or too long!'; } }
We marked our class with
@ValidatorConstraint
decorator. You can also supply a validation constraint name - this name will be used as "error type" in ValidationError. If you will not supply a constraint name - it will be auto-generated.Our class must implement
ValidatorConstraintInterface