What if you could write your validation rules once and have them magically work everywhere?
Why Custom validation decorators in NestJS? - Purpose & Use Cases
Imagine you have many forms in your app, and you need to check if user inputs follow specific rules like "must be a valid phone number" or "password must have special characters".
You write the same validation code again and again in every controller or service.
Manually repeating validation code everywhere is tiring and easy to forget or make mistakes.
It also makes your code messy and hard to update if rules change.
Custom validation decorators let you write your validation logic once and reuse it everywhere by just adding a simple tag on your data fields.
This keeps your code clean, consistent, and easy to maintain.
if (!isValidPhone(user.phone)) { throw new Error('Invalid phone'); }
@IsPhoneNumber() phone: string;
You can create clear, reusable, and easy-to-read validation rules that automatically check user input wherever needed.
In a signup form, instead of writing phone number checks in the controller, you just add @IsPhoneNumber() on the phone field in your DTO, and NestJS handles the rest.
Manual validation is repetitive and error-prone.
Custom decorators let you write validation once and reuse it.
This makes your code cleaner and easier to maintain.
Practice
Solution
Step 1: Understand validation decorators
Validation decorators in NestJS are used to check if data meets certain rules before processing.Step 2: Purpose of custom validation decorators
Custom validation decorators let you create your own specific rules that are not covered by built-in validators.Final Answer:
To define your own rules for validating data inputs -> Option DQuick Check:
Custom validation decorators = custom data rules [OK]
- Confusing validation decorators with routing or database features
- Thinking custom decorators generate schemas automatically
- Assuming they handle HTTP requests directly
Solution
Step 1: Identify the decorator for custom validators
In NestJS, custom validation classes must be marked with @ValidatorConstraint to tell the system they are validators.Step 2: Differentiate from other decorators
@Controller is for routing, @Injectable for services, and @Module for modules, not for validation.Final Answer:
Use @ValidatorConstraint decorator on the class -> Option CQuick Check:
@ValidatorConstraint marks custom validator classes [OK]
- Using @Controller or @Module instead of @ValidatorConstraint
- Forgetting to decorate the validator class
- Confusing service decorators with validation decorators
import { ValidatorConstraint, ValidatorConstraintInterface } from 'class-validator';
@ValidatorConstraint({ name: 'isAlphaOnly', async: false })
export class IsAlphaOnly implements ValidatorConstraintInterface {
validate(text: string) {
return /^[A-Za-z]+$/.test(text);
}
defaultMessage() {
return 'Text must contain only letters';
}
}Solution
Step 1: Analyze the regex in validate method
The regex /^[A-Za-z]+$/ matches only strings with letters from start to end, no numbers allowed.Step 2: Check the test string 'abc123'
This string contains numbers '123', so it does not match the regex and validate returns false.Final Answer:
Validation fails because 'abc123' contains numbers -> Option AQuick Check:
Regex /^[A-Za-z]+$/ rejects numbers [OK]
- Assuming partial matches pass validation
- Ignoring numbers in the test string
- Thinking regex allows digits when it does not
import { registerDecorator, ValidationOptions, ValidatorConstraint, ValidatorConstraintInterface } from 'class-validator';
@ValidatorConstraint({ async: true })
export class IsEvenNumber implements ValidatorConstraintInterface {
validate(value: number) {
return value % 2 === 0;
}
}
export function IsEven(validationOptions?: ValidationOptions) {
return function (object: Object, propertyName: string) {
registerDecorator({
name: 'isEven',
target: object.constructor,
propertyName: propertyName,
options: validationOptions,
validator: IsEvenNumber,
});
};
}Solution
Step 1: Check async flag and validate method
The @ValidatorConstraint decorator has async: true, so validate must return a Promise<boolean>.Step 2: Validate method returns boolean, not Promise
Currently, validate returns a boolean directly, which conflicts with async: true.Final Answer:
The validate method should return a Promise because async is true -> Option AQuick Check:
async: true requires Promise return [OK]
- Returning boolean instead of Promise when async is true
- Omitting @ValidatorConstraint decorator
- Misusing registerDecorator parameters
Solution
Step 1: Understand custom validation structure
Custom validators require a class decorated with @ValidatorConstraint implementing validate() method.Step 2: Use registerDecorator to link the class to a decorator function
This function applies the validator to the property in the DTO.Step 3: Implement validate() with regex checking uppercase and digit presence
This ensures the string meets both conditions in one check.Final Answer:
Create a class with @ValidatorConstraint, implement validate() using regex, then use registerDecorator in a function to apply it -> Option BQuick Check:
Custom validator class + registerDecorator function = correct approach [OK]
- Skipping the validator class and using only registerDecorator
- Trying to use services instead of decorators for validation
- Assuming built-in decorators cover combined conditions
Ready to prove it?
Pass the test and this pattern moves unknown
