NestJS Custom Payload Validation
Tags: node, nestjs, security, typescript
Published: 2022-03-15 08:52:57

This is a follow-up post to my previous blog [NestJS Payload Validation](https://dev.to/shubham_kadam/nestjs-payload-validation-anf). In this blog-post I’ll talk about how we can implement our own custom payload validation. That is, there might be a scenario where you would like to add your own logic to validate particular payload value and the class-validator package provides us with this ability.

> Before we begin, here's the [github repo](https://github.com/shubham16kadam/nestjs-payload-validation/tree/custom-payload-validator) for the project 
 which has below mentioned code examples.

In order to perform custom payload validation we need to implement `ValidatorConstraintInterface` interface and its `validate` method and post implementing the interface we need to apply `@ValidatorConstraint` decorator to the class (which implements the interface). For instance, lets say we have deployed an api which expects payload with employee’s age information, here’s an example:
```
{
  "age": 24
}
```
And the DTO for the same:

```
export class EmployeeAgeDetails {
  age: number;
}
```
The `age` property needs to have some restriction, like for instance, age should be an integer value and should be greater than (>=) 18 and less than (<=) 65.

> I know in real world we would expect much more info than just employee’s age, but lets keep things simple so we understand better and quicker!

_Fyi, we can perform the above mention validation directly with [validation decorators](https://github.com/typestack/class-validator#validation-decorators) provided by the [class-validator](https://github.com/typestack/class-validator#class-validator) as well. Please checkout my [NestJS Payload Validation](https://dev.to/shubham_kadam/nestjs-payload-validation-anf) blog for example._

Lets see some code examples to demonstrate above case:

The class which implements `ValidatorConstraintInterface` interface, lets call it `CustomAgeLimitValidator`, here’s code for the same:
```
import {
  ValidatorConstraint,
  ValidatorConstraintInterface,
} from 'class-validator';

@ValidatorConstraint({ name: 'CustomAgeLimitValidator', async: false })
export class CustomAgeLimitValidator implements ValidatorConstraintInterface {
  validate(value: any): boolean {
    // Add validation logic here
    if (value < 18 || value > 65) return false;
    return true;
  }
}
```
The `name` argument in `@ValidatorConstraint` decorator represents the ‘error type’ and if not provided, the value will be generated automatically. The validation can also be asynchronous, hence the param `async` is used in decorator `@ValidatorConstraint` (please checkout the [documentation](https://www.npmjs.com/package/class-validator#custom-validation-classes) for more info).

Here’s how you apply the `CustomAgeLimitValidator` validator to `EmployeeAgeDetails` DTO:
```
import {
  IsInt,
  IsNotEmpty,
  Validate,
} from 'class-validator';
import { CustomAgeLimitValidator } from '../custome-payload-validators/age-limit-validator';

export class EmployeeAgeDetails {
  @IsNotEmpty()
  @IsInt()
  @Validate(CustomAgeLimitValidator)
  age: number;
}
```
The  [validation decorators](https://github.com/typestack/class-validator#validation-decorators) provided by the [class-validator](https://github.com/typestack/class-validator#class-validator) provides us with the ability to pass our own error message. In our case, here’s how we will do it:
```
@Validate(CustomAgeLimitValidator, {message: 'Age limit violation! Employee age should be between 18 and 65 years'})
```
With these changes if we try to feed our endpoint with below payload:

```
{
  "age": 16
}
```

It would cause below error:

```
{
    "statusCode": 400,
    "message": [
        "Age limit violation! Employee age should be between 18 and 65 years"
    ],
    "error": "Bad Request"
}
```
Along with the ability to provide error message while applying validation decorators we can also provide default error message incase we forget or don’t feel the need to provide error message while applying decorator. To implement this case, we need to add optional `defaultMessage` method inside our `CustomAgeLimitValidator` class. Here’s the code example:
```
import {
  ValidatorConstraint,
  ValidatorConstraintInterface,
  ValidationArguments,
} from 'class-validator';

@ValidatorConstraint({ name: 'CustomAgeLimitValidator', async: false })
export class CustomAgeLimitValidator implements ValidatorConstraintInterface {
  validate(value: any): boolean {
    // Add validation logic here
    if (value < 18 || value > 65) return false;
    return true;
  }

  // optional method
  defaultMessage(args: ValidationArguments) {
    // Provide default error message if validation failed
    return `Age value ${args.value} caused age limit violated! Employee age should be between 18 and 65 years`;
  }
}
```
And if we feed the api with invalid age value
```
{
  "age": 16
}
```
with above changes, the output error message would be:
```
{
    "statusCode": 400,
    "message": [
        "Age value 16 caused age limit violation! Employee age should be between 18 and 65 years"
    ],
    "error": "Bad Request"
}
```
And we are done! Hope you guys enjoyed it.

![Adios](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7w3f7m2hkvomgvxjl773.gif)
**Stay tuned, Thank You!**