Custom Validation¶
Custom Validation Classes¶
Creating a Custom Validator¶
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;
}
defaultMessage(args: ValidationArguments) {
return 'Text ($value) is too short or too long!';
}
}
We marked our class with the @ValidatorConstraint decorator. You can also supply a validation
constraint name — this name will be used as the "error type" in ValidationError. If you don't supply a
constraint name, it will be auto-generated.
Our class must implement the ValidatorConstraintInterface interface and its validate method, which
defines the validation logic. If validation succeeds, the method returns true, otherwise false.
Custom validators can be asynchronous — if you want to perform validation after some asynchronous
operation, simply return a Promise<boolean> from validate.
The optional defaultMessage method defines a default error message, used when the decorator's
implementation doesn't set its own error message.
Using Custom Validator¶
import { Validate } from 'class-validator';
import { CustomTextLength } from './CustomTextLength';
export class Post {
@Validate(CustomTextLength, {
message: 'Title is too short or long!'
})
title: string;
}
You can also pass constraints to your validator:
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 the 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 decorator. It's the most elegant way of using custom validations. Let's
create a decorator called @IsLongerThan:
Creating a Custom Decorator¶
import { registerDecorator, ValidationOptions, ValidationArguments } from 'class-validator';
export function IsLongerThan(property: string, validationOptions?: ValidationOptions) {
return function (object: Object, propertyName: string) {
registerDecorator({
name: 'isLongerThan',
target: object.constructor,
propertyName: propertyName,
constraints: [property],
options: validationOptions,
validator: {
validate(value: any, args: ValidationArguments) {
const [relatedPropertyName] = args.constraints;
const relatedValue = (args.object as any)[relatedPropertyName];
return typeof value === 'string' &&
typeof relatedValue === 'string' &&
value.length > relatedValue.length;
}
}
});
};
}
Using Custom Decorator¶
import { IsLongerThan } from './IsLongerThan';
export class Post {
title: string;
@IsLongerThan('title', {
message: 'Text must be longer than the title'
})
text: string;
}
In your custom decorators you can also use ValidatorConstraint. Let's create another custom
validation decorator called IsUserAlreadyExist:
import {
registerDecorator,
ValidationOptions,
ValidatorConstraint,
ValidatorConstraintInterface,
ValidationArguments,
} from 'class-validator';
@ValidatorConstraint({ async: true })
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({
target: object.constructor,
propertyName: propertyName,
options: validationOptions,
constraints: [],
validator: IsUserAlreadyExistConstraint,
});
};
}
Note that we marked our constraint as async by adding { async: true } to the validation options. Then
put it to use:
import { IsUserAlreadyExist } from './IsUserAlreadyExist';
export class User {
@IsUserAlreadyExist({
message: 'User $value already exists. Choose another name.',
})
name: string;
}
Need documentation like this for your own product?
This site was built by Sonicar Tech LLC — we help SaaS, B2B, Enterprise, FinTech, and AI companies launch professional, docs-as-code documentation 60% faster and cheaper than building an in-house team, with first drafts delivered in 1 week.