class-validator
Allows to use decorator and non-decorator based validation. Internally uses validator.js to perform validation.
Installation
-
Install module:
npm install class-validator --save
-
ES6 features are used, so you may want to install es6-shim too:
npm install es6-shim --save
and use it somewhere in the global place of your app:
- for nodejs:
require("es6-shim")
in your app's entry point (for example in app.ts
) - for browser:
<script src="node_modules/es6-shim/es6-shim.js">
in your index.html
For node.js users this step is only required if you are using old versions of node.
Usage
Create your class and put some validation decorators on the properties you want to validate:
import {validate, Contains, IsInt, IsLength, IsEmail, IsFQDN, IsDate} from "class-validator";
export class Post {
@IsLength(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";
post.text = "this is a great post about hell world";
post.rating = 11;
post.email = "google.com";
post.site = "googlecom";
validate(post).then(errors => {
if (errors.length > 0) {
console.log("validation failed. errors: ", errors);
} else {
console.log("validation succeed");
}
});
Validation messages
You can specify validation message in the decorator options and that message will be returned in ValidationError
returned by validate
method in the case if validation for this field fail.
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 right now$property
- name of the object's property being validated right now$target
- name of the object's class being validated right now$constraint1
, $constraint2
, ... $constraintN
- constraints defined by specific validation type
Example of usage:
import {MinLength, MaxLength} from "class-validator";
export class Post {
@MinLength(10, {
message: "Title is too short. Minimal length is $constraint1 characters, but actual is $value"
})
@MaxLength(50, {
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 way allows 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, minimal length is 1 character";
} else {
return "Too short, minimal length is " + args.constraints[0] + " characters";
}
}
})
title: string;
}
Message function accepts ValidationArguments
which contains following information:
value
- the value that is being validated right nowconstraints
- array of constraints defined by specific validation typetargetName
- name of the object's class being validated right nowobject
- object that is being validatedproperty
- name of the object's property being validated right now
Values are being passed to this function, so
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 nested objects
If your object contains nested objects and you want validator to perform their validation too, then you need to
use @ValidateNested()
decorator:
import {ValidateNested} from "class-validator";
export class Post {
@ValidateNested()
user: User;
}
Skipping missing properties
Sometimes you may want to skip validation of the properties that does 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 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.
import {validate, MinNumber, Length} from "class-validator";
export class User {
@MinNumber(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"]
});
validate(user, {
groups: ["admin"]
});
validate(user, {
groups: ["registration", "admin"]
});
validate(user, {
groups: []
});
Custom validation classes
If you have custom validation logic you have a way to do it - you can create a Constraint class:
-
First create a file, lets say CustomTextLength.ts
, and create there a new class:
import {ValidatorConstraint, ValidatorConstraintInterface, ValidationArguments} from "class-validator";
@ValidatorConstraint()
export class CustomTextLength implements ValidatorConstraintInterface {
validate(text: string, args: ValidationArguments) {
return text.length > 1 && text.length < 10;
}
defaultMessage(args: ValidationArguments) {
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
interface and its validate
method,
which defines validation logic. If validation succeed method return true, otherwise false.
Custom validator can be asynchronous, if you want to perform validation after some asynchronous
operations, simply return a promise with boolean inside in validate
method.
Also we defined optional method defaultMessage
which defines a default error message,
in the case if decorator's user didn't set error message.
-
Then you can use your new validation constraint in your class:
import {Validate} from "class-validator";
import {CustomTextLength} from "./CustomTextLength";
export class Post {
@Validate(CustomTextLength, {
message: "Title is too short or long!"
})
title: string;
}
Here we set our newly created CustomTextLength
validation constraint for Post.title
.
-
And use validator as usual:
import {validate} from "class-validator";
validate(post).then(errors => {
});
You can also send constraints to your validator, like this:
import {Validate} from "class-validator";
import {CustomTextLength} from "./CustomTextLength";
export class Post {
@Validate(CustomTextLength, [3, 20], {
message: "Wrong post title"
})
title: string;
}
And use them from validationArguments
object:
import {ValidationArguments, ValidatorConstraint, ValidatorConstraintInterface} from "class-validator";
@ValidatorConstraint()
export class CustomTextLength implements ValidatorConstraintInterface {
validate(text: string, validationArguments: ValidationArguments) {
return text.length > validationArguments.constraints[0] && text.length < validationArguments.constraints[1];
}
}
Custom validation decorators
You can also create a custom decorators. Its the most elegant way of using a custom validations.
Lets create a decorator called @IsLongerThen
:
-
Create a decorator itself:
import {registerDecorator, ValidationOptions} from "class-validator";
export function IsLongerThen(property: string, validationOptions?: ValidationOptions) {
return function (object: Object, propertyName: string) {
registerDecorator(object, propertyName, validationOptions, [property], "is_longer_then", (value, args) => {
const [relatedPropertyName] = args.constraints;
const relatedValue = (args.object as any)[relatedPropertyName];
return typeof value === "string" &&
typeof relatedValue === "string" &&
value.length > relatedValue.length;
});
};
}
-
Put it on use:
import {IsLongerThen} from "./IsLongerThen";
export class Post {
title: string;
@IsLongerThen("title", {
message: "Text must be longer the title"
})
text: string;
}
In your custom decorators you can also use ValidationConstraint
.
Lets create another custom validation decorator called IsUserAlreadyExist
:
-
Create a ValidationConstraint and decorator:
import {registerDecorator, ValidationOptions, ValidatorConstraint, ValidatorConstraintInterface, ValidationArguments} from "class-validator";
@ValidatorConstraint()
export class IsUserAlreadyExistConstraint implements ValidatorConstraintInterface {
validate(userName: any, args: ValidationArguments) {
return UserRepository.findOneByName(userName).then(user => {
if (user) return false;
return true;
});
}
}
export function IsUserAlreadyExist(validationOptions?: ValidationOptions) {
return function (object: Object, propertyName: string) {
registerDecorator(object, propertyName, validationOptions, [], IsUserAlreadyExistConstraint);
};
}
-
And put it on use:
import {IsUserAlreadyExist} from "./IsUserAlreadyExist";
export class User {
@IsUserAlreadyExist({
message: "User $value already exist. Choose another name."
})
name: string;
}
Using service container
Validator supports service container in the case if want to inject dependencies into your custom validator constraint
classes. Here is example how to integrate it with typedi:
import {Container} from "typedi";
import {useContainer} from "class-validator";
useContainer(Container);
let validator = Container.get(Validator);
Manual validation
There are several method exist in the Validator that allows to perform non-decorator based validation:
import {Validator} from "class-validator";
const validator = new Validator();
validator.isDefined(value);
validator.equals(value, comparison);
validator.notEquals(value, comparison);
validator.empty(value);
validator.notEmpty(value);
validator.isIn(value, possibleValues);
validator.isNotIn(value, possibleValues);
validator.isBoolean(value);
validator.isDate(value);
validator.isString(value);
validator.isNumber(value);
validator.isInt(value);
validator.isDecimal(value);
validator.divisibleBy(value, num);
validator.isPositive(value);
validator.isNegative(value);
validator.greaterThen(firstNumber, secondNumber);
validator.lessThen(firstNumber, secondNumber);
validator.minDate(date, minDate);
validator.maxDate(date, minDate);
validator.isBooleanString(str);
validator.isDateString(str);
validator.isNumberString(str);
validator.contains(str, seed);
validator.notContains(str, seed);
validator.isAlpha(str);
validator.isAlphanumeric(str);
validator.isAscii(str);
validator.isBase64(str);
validator.isByteLength(str, min, max);
validator.isCreditCard(str);
validator.isCurrency(str, options);
validator.isEmail(str, options);
validator.isFQDN(str, options);
validator.isFullWidth(str);
validator.isHalfWidth(str);
validator.isVariableWidth(str);
validator.isHexColor(str);
validator.isHexadecimal(str);
validator.isIP(str, version);
validator.isISBN(str, version);
validator.isISIN(str);
validator.isISO8601(str);
validator.isJSON(str);
validator.isLowercase(str);
validator.isMobilePhone(str, locale);
validator.isMongoId(str);
validator.isMultibyte(str);
validator.isSurrogatePair(str);
validator.isURL(str, options);
validator.isUUID(str, version);
validator.isUppercase(str);
validator.length(str, min, max);
validator.minLength(str, min);
validator.maxLength(str, max);
validator.matches(str, pattern, modifiers);
validator.arrayContains(array, values);
validator.arrayNotContains(array, values);
validator.arrayNotEmpty(array);
validator.arrayMinSize(array, min);
validator.arrayMaxSize(array, max);
validator.arrayUnique(array);
Validation decorators
Decorator | Description |
---|
Common validation decorators | |
@IsDefined(value: any) | Checks if value is defined ("!==undefined"). This is the only decorator that ignores skipMissingProperties validation option. |
@Equals(comparison: any) | Checks if value equals ("===") comparison. |
@NotEquals(comparison: any) | Checks if value not equal ("!==") comparison. |
@Empty() | Checks if given value is empty (=== '', === null, === undefined). |
@NotEmpty() | Checks if given value is not empty (!== '', !== null, !== undefined). |
@In(values: any[]) | Checks if value is in a array of allowed values. |
@NotIn(values: any[]) | Checks if value is not in a array of disallowed values. |
Type validation decorators | |
@IsBoolean() | Checks if a value is a boolean. |
@IsDate() | Checks if the string is a date. |
@IsString() | Checks if the string is a string. |
@IsNumber() | Checks if the string is a number. |
@IsInt() | Checks if the value is an integer number. |
@IsDecimal() | Checks if the value represents a decimal number, such as 0.1, .3, 1.1, 1.00003, 4.0, etc. |
Number validation decorators | |
@DivisibleBy(num: number) | Checks if the value is a number that's divisible by another. |
@IsPositive() | Checks if the value is a positive number. |
@IsNegative() | Checks if the value is a negative number. |
@GreaterThen(num: number) | Checks if the given number is greater then given number. |
@LessThen(num: number) | Checks if the given number is less then given number. |
Date validation decorators | |
@MinDate(date: Date) | Checks if the value is a date that's after the specified date. |
@MaxDate(date: Date) | Checks if the value is a date that's before the specified date. |
String-type validation decorators | |
@IsBooleanString() | Checks if a string is a boolean (e.g. is "true" or "false"). |
@IsDateString() | Checks if a string is a date. |
@IsNumberString() | Checks if a string is a number. |
String validation decorators | |
@Contains(seed: string) | Checks if the string contains the seed. |
@NotContains(seed: string) | Checks if the string not contains the seed. |
@IsAlpha() | Checks if the string contains only letters (a-zA-Z). |
@IsAlphanumeric() | Checks if the string contains only letters and numbers. |
@IsAscii() | Checks if the string contains ASCII chars only. |
@IsBase64() | Checks if a string is base64 encoded. |
@IsByteLength(min: number, max?: number) | Checks if the string's length (in bytes) falls in a range. |
@IsCreditCard() | Checks if the string is a credit card. |
@IsCurrency(options?: IsCurrencyOptions) | Checks if the string is a valid currency amount. |
@IsEmail(options?: IsEmailOptions) | Checks if the string is an email. |
@IsFQDN(options?: IsFQDNOptions) | Checks if the string is a fully qualified domain name (e.g. domain.com). |
@IsFullWidth() | Checks if the string contains any full-width chars. |
@IsHalfWidth() | Checks if the string contains any half-width chars. |
@IsVariableWidth() | Checks if the string contains a mixture of full and half-width chars. |
@IsHexColor() | Checks if the string is a hexadecimal color. |
@IsHexadecimal() | Checks if the string is a hexadecimal number. |
`@IsIP(version?: "4" | "6")` |
`@IsISBN(version?: "10" | "13")` |
@IsISIN() | Checks if the string is an ISIN (stock/security identifier). |
@IsISO8601() | Checks if the string is a valid ISO 8601 date. |
@IsJSON() | Checks if the string is valid JSON. |
@IsLowercase() | Checks if the string is lowercase. |
@IsMobilePhone(locale: string) | Checks if the string is a mobile phone number. |
@IsMongoId() | Checks if the string is a valid hex-encoded representation of a MongoDB ObjectId. |
@IsMultibyte() | Checks if the string contains one or more multibyte chars. |
@IsNumericString() | Checks if the string is numeric. |
@IsSurrogatePair() | Checks if the string contains any surrogate pairs chars. |
@IsUrl(options?: IsURLOptions) | Checks if the string is an url. |
`@IsUUID(version?: "3" | "4" |
@IsUppercase() | Checks if the string is uppercase. |
@Length(min: number, max?: number) | Checks if the string's length falls in a range. |
@MinLength(min: number) | Checks if the string's length is not less then given number. |
@MaxLength(max: number) | Checks if the string's length is not more then given number. |
@Matches(pattern: RegExp, modifiers?: string) | Checks if string matches the pattern. Either matches('foo', /foo/i) or matches('foo', 'foo', 'i'). |
Array validation decorators | |
@ArrayContains(values: any[]) | Checks if array contains all values from the given array of values. |
@ArrayNotContains(values: any[]) | Checks if array does not contain any of the given values. |
@ArrayNotEmpty() | Checks if given array is not empty. |
@ArrayMinSize(min: number) | Checks if array's length is as minimal this number. |
@ArrayMaxSize(max: number) | Checks if array's length is as maximal this number. |
@ArrayUnique() | Checks if all array's values are unique. Comparison for objects is reference-based. |
Defining validation schema without decorators
You can define your validation schemas without decorators:
- you can define it in the separate object
- you can define it in the
.json
file
This feature maybe useful in the cases if:
- are using es5/es6 and don't have decorators available
- you don't have a classes, and instead using interfaces
- you don't want to use model at all
- you want to have a validation schema separate of your model
- you want beautiful json-schema based validation models
- you simply hate decorators
Here is an example of using it:
-
Create a schema object:
import {ValidationSchema} from "class-validator";
export let UserValidationSchema: ValidationSchema = {
name: "myUserSchema",
properties: {
firstName: [{
type: "min_length",
constraints: [2]
}, {
type: "max_length",
constraints: [20]
}],
lastName: [{
type: "min_length",
constraints: [2]
}, {
type: "max_length",
constraints: [20]
}],
email: [{
type: "is_email"
}]
}
};
Same schema can be provided in .json
file, depend on your wish.
-
Register your schema:
import {registerSchema} from "class-validator";
import {UserValidationSchema} from "./UserValidationSchema";
registerSchema(schema);
Better to put this code in a global place, maybe when you bootstrap your application, for example in app.ts
.
-
Validate your object using validation schema:
import {validate} from "class-validator";
const user = { firstName: "Johny", secondName: "Cage", email: "johny@cage.com" };
validate("myUserSchema", user).then(errors => {
if (errors.length > 0) {
console.log("Validation failed: ", errors);
} else {
console.log("Validation succeed.");
}
});
That's it. Here "myUserSchema"
is the name of our validation schema.
validate
method will perform validation based on this schema
Samples
Take a look on samples in ./sample for more examples of
usages.
Release Notes
0.4.0 [BREAKING CHANGES]
- everything should be imported from "class-validator" main entry point now
ValidatorInterface
has been renamed to ValidatorConstraintInterface
- contain can be set in the main entry point now
- some decorator's names changed. Be aware of this
- fixed all decorators that should not work only with strings
- added few more non-string decorators
- validator now returns array of ValidationError instead of ValidationErrorInterface. Removed old ValidationError
- removed all other validation methods except
validator.validate
- finally validate method is async now, so custom async validations are supported now
- added ability to validate inherited properties
- added support of separate validation schemas
- added support of default validation messages
- added support of special tokens in validation messages
- added support of message functions in validation options
- added support of custom decorators
0.3.0
- package has changed its name from
validator.ts
to class-validator
. - sanitation functionality has been removed from this library. Use class-sanitizer instead.