0
0
NestJSframework~15 mins

Custom validation decorators in NestJS - Deep Dive

Choose your learning style9 modes available
Overview - Custom validation decorators
What is it?
Custom validation decorators in NestJS are special functions that you create to check if data meets certain rules before your app uses it. They help you add your own checks beyond the built-in ones. This makes sure the data is correct and safe. You attach these decorators to class properties to automatically validate input.
Why it matters
Without custom validation decorators, you would have to write repetitive and scattered code to check data everywhere. This can cause mistakes and make your app less reliable. Custom decorators keep validation clean, reusable, and close to the data structure, making your app easier to maintain and safer for users.
Where it fits
Before learning custom validation decorators, you should understand basic TypeScript decorators and how NestJS uses class-validator for validation. After this, you can explore advanced validation techniques, error handling, and integrating validation with request pipelines.
Mental Model
Core Idea
A custom validation decorator is a reusable rule attached to data properties that automatically checks if the data is valid before use.
Think of it like...
It's like putting a custom lock on a mailbox that only accepts letters of a certain size or shape, ensuring only the right mail gets in without checking each letter manually.
┌───────────────────────────────┐
│        Data Class             │
│ ┌───────────────┐            │
│ │ Property A    │◄── Custom Validation Decorator
│ └───────────────┘            │
│ ┌───────────────┐            │
│ │ Property B    │◄── Built-in Validation Decorator
│ └───────────────┘            │
└───────────────────────────────┘
          │
          ▼
  Validation Engine runs checks
          │
          ▼
  Accept or reject data input
Build-Up - 7 Steps
1
FoundationUnderstanding basic decorators
🤔
Concept: Learn what decorators are and how they attach behavior to classes or properties.
Decorators are special functions in TypeScript that you place above classes, methods, or properties. They let you add extra behavior or metadata. For example, a property decorator can mark a property to be validated later.
Result
You can mark parts of your code with decorators that add extra meaning or rules.
Understanding decorators is key because custom validation decorators are built on this concept to add validation rules directly to data properties.
2
FoundationUsing built-in validation decorators
🤔
Concept: See how NestJS uses class-validator decorators to check data automatically.
NestJS uses the class-validator library, which provides decorators like @IsString() or @IsInt() to check if data matches expected types or formats. When you apply these decorators to class properties, NestJS validates incoming data against these rules.
Result
Data is automatically checked for common rules without extra code.
Knowing built-in decorators shows the pattern that custom decorators will follow, making validation declarative and centralized.
3
IntermediateCreating a simple custom validator
🤔Before reading on: do you think a custom validator needs to be a class or can it be a simple function? Commit to your answer.
Concept: Learn how to write a basic custom validation decorator using class-validator's ValidatorConstraint and register it.
You create a class that implements ValidatorConstraintInterface with a validate method for your rule. Then, you create a decorator function that uses @ValidatorConstraint and @Validate to link your rule to a property. For example, checking if a string contains only vowels.
Result
You get a new decorator that you can use like @IsOnlyVowels() on properties to enforce your custom rule.
Understanding that custom validators are classes with a validate method helps you see how flexible validation can be beyond built-in rules.
4
IntermediateAdding error messages and options
🤔Before reading on: do you think custom validators can provide custom error messages? Commit to your answer.
Concept: Learn how to customize the error message shown when validation fails and pass options to your decorator.
Inside your validator class, implement the defaultMessage method to return a custom error string. Also, your decorator function can accept options like validation groups or conditional checks. This makes your validator more user-friendly and flexible.
Result
When validation fails, users see clear, meaningful messages tailored to your rule.
Custom error messages improve user experience and debugging, making your validation rules more professional.
5
IntermediateUsing async validation decorators
🤔Before reading on: do you think validation can be asynchronous, like checking a database? Commit to your answer.
Concept: Learn how to create validators that perform asynchronous checks, such as verifying uniqueness in a database.
Implement the validate method as async and return a Promise. NestJS and class-validator support async validation, so your decorator can wait for external checks before deciding if data is valid.
Result
You can validate data that depends on external systems, not just static rules.
Knowing async validation expands your ability to enforce complex real-world rules that depend on dynamic data.
6
AdvancedCombining multiple custom decorators
🤔Before reading on: do you think multiple decorators on one property run independently or affect each other? Commit to your answer.
Concept: Learn how multiple custom and built-in decorators work together on the same property and how to control their order and interaction.
When you stack decorators, each runs its validation independently. You can control validation flow with groups or conditional decorators. Understanding this helps avoid conflicts and ensures all rules apply correctly.
Result
Your data passes through all intended checks, improving robustness.
Knowing how decorators combine prevents subtle bugs where some validations might be skipped or overridden.
7
ExpertInternals of custom validation execution
🤔Before reading on: do you think validation decorators run at compile time or runtime? Commit to your answer.
Concept: Understand how NestJS and class-validator collect metadata from decorators and run validation at runtime using reflection.
Decorators add metadata to class properties using Reflect Metadata API. At runtime, the validation engine reads this metadata, creates validator instances, and calls their validate methods. This dynamic process allows flexible and powerful validation without changing your class logic.
Result
You see how your decorators translate into runtime checks seamlessly.
Understanding the runtime mechanism helps debug complex validation issues and optimize performance.
Under the Hood
Custom validation decorators work by attaching metadata to class properties using TypeScript's Reflect Metadata API. When validation runs, NestJS reads this metadata to find which validators to execute. Each validator class has a validate method that checks the value and returns true or false (or a Promise for async). The validation engine aggregates results and reports errors if any rule fails.
Why designed this way?
This design separates validation logic from business logic, making code cleaner and reusable. Using metadata allows decorators to be declarative and non-intrusive. The class-validator library was chosen for its flexibility and integration with NestJS, avoiding manual validation code and enabling complex rules easily.
┌───────────────┐       ┌───────────────────────┐
│  Class with   │       │  Custom Validator      │
│ Decorated     │──────▶│  Class with validate() │
│ Properties    │       └───────────────────────┘
│ (Metadata)    │
└──────┬────────┘
       │ Reflect Metadata API
       ▼
┌───────────────────────┐
│ Validation Engine     │
│ Reads metadata, calls │
│ validate() methods    │
└─────────┬─────────────┘
          │
          ▼
┌───────────────────────┐
│ Validation Result     │
│ Accept or Reject Data │
└───────────────────────┘
Myth Busters - 4 Common Misconceptions
Quick: Do custom validation decorators run automatically without calling any validation function? Commit to yes or no.
Common Belief:Custom validation decorators automatically validate data as soon as it is assigned.
Tap to reveal reality
Reality:Decorators only add metadata; validation happens when you explicitly call the validation function like validate() or use ValidationPipe in NestJS.
Why it matters:Assuming automatic validation can cause bugs where invalid data passes unnoticed because validation was never triggered.
Quick: Can a custom validation decorator change the value of the property it validates? Commit to yes or no.
Common Belief:Custom validation decorators can modify the property value during validation.
Tap to reveal reality
Reality:Validation decorators only check values; they do not modify them. Changing data should be done separately.
Why it matters:Expecting decorators to change data can lead to confusion and bugs where data is not transformed as intended.
Quick: Is it safe to perform long-running operations inside a custom validator's validate method? Commit to yes or no.
Common Belief:You can do any operation, including heavy computations or long database calls, inside validate without issues.
Tap to reveal reality
Reality:Long-running operations can slow down validation and block requests. Async validation is supported but should be used carefully with caching or limits.
Why it matters:Ignoring performance can degrade app responsiveness and user experience.
Quick: Do multiple decorators on the same property run in a guaranteed order? Commit to yes or no.
Common Belief:Decorators run in the order they are written on the property.
Tap to reveal reality
Reality:The order of validation execution is not guaranteed and should not be relied upon; all validations run independently.
Why it matters:Relying on order can cause unpredictable validation results and hard-to-find bugs.
Expert Zone
1
Custom validators can be combined with validation groups to apply different rules in different contexts, a subtle but powerful feature.
2
Async validators must return Promises and handle rejection properly to avoid unhandled exceptions during validation.
3
Using the ValidatorConstraint decorator with { async: true } signals the validation engine to await the validate method, a detail often missed.
When NOT to use
Avoid custom validation decorators when simple built-in decorators suffice or when validation logic is better handled at the service or database layer, such as complex business rules or cross-entity checks. Use middleware or pipes for request-level validation instead.
Production Patterns
In production, custom validators are often used for domain-specific rules like checking unique usernames, validating complex formats, or enforcing business constraints. They are combined with ValidationPipe globally or per-route to ensure consistent validation. Error messages are localized and structured for client consumption.
Connections
Aspect-Oriented Programming (AOP)
Custom validation decorators are a form of AOP where validation logic is separated from business logic and applied declaratively.
Understanding AOP helps grasp how decorators inject behavior cleanly without cluttering core code.
Metadata Reflection
Custom validation decorators rely on metadata reflection to store and retrieve validation rules at runtime.
Knowing how reflection works clarifies how decorators communicate with the validation engine dynamically.
Quality Control in Manufacturing
Validation decorators act like quality control checkpoints ensuring each product (data) meets standards before moving forward.
Seeing validation as quality control highlights its role in preventing defects early and maintaining system integrity.
Common Pitfalls
#1Writing a custom validator without implementing ValidatorConstraintInterface.
Wrong approach:class MyValidator { validate(value: any) { return typeof value === 'string'; } } function IsMyValid() { return Validate(MyValidator); }
Correct approach:import { ValidatorConstraint, ValidatorConstraintInterface, Validate } from 'class-validator'; @ValidatorConstraint({ name: 'isMyValid', async: false }) class MyValidator implements ValidatorConstraintInterface { validate(value: any) { return typeof value === 'string'; } } function IsMyValid() { return Validate(MyValidator); }
Root cause:Not implementing the interface means the validation engine cannot recognize or properly use the validator.
#2Calling validation decorators without using ValidationPipe or validate function.
Wrong approach:class UserDto { @IsEmail() email: string; } // No validation call anywhere
Correct approach:class UserDto { @IsEmail() email: string; } // Use ValidationPipe in controller or call validate(userDto) explicitly
Root cause:Decorators only add metadata; validation must be triggered explicitly.
#3Performing side effects or data mutation inside validate method.
Wrong approach:validate(value: any) { this.log(value); // side effect value = value.trim(); // mutation return value.length > 0; }
Correct approach:validate(value: any) { return typeof value === 'string' && value.trim().length > 0; }
Root cause:Validation should be pure to avoid unexpected behavior and maintain predictability.
Key Takeaways
Custom validation decorators let you create reusable, declarative rules to check data correctness in NestJS.
They work by attaching metadata to class properties and running validation logic at runtime through the class-validator library.
You must explicitly trigger validation using ValidationPipe or validate functions; decorators alone do not validate automatically.
Custom validators can be synchronous or asynchronous, allowing checks against external systems like databases.
Understanding the internal metadata and validation flow helps you write robust, maintainable, and performant validation logic.