DEV Community

Cover image for NestJS Custom Payload Validation
Shubham Kadam
Shubham Kadam

Posted on • Updated on

NestJS Custom Payload Validation

This is a follow-up post to my previous blog NestJS Payload Validation. 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 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
}
Enter fullscreen mode Exit fullscreen mode

And the DTO for the same:

export class EmployeeAgeDetails {
  age: number;
}
Enter fullscreen mode Exit fullscreen mode

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 provided by the class-validator as well. Please checkout my NestJS Payload Validation 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;
  }
}
Enter fullscreen mode Exit fullscreen mode

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 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;
}
Enter fullscreen mode Exit fullscreen mode

The validation decorators provided by the 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'})
Enter fullscreen mode Exit fullscreen mode

With these changes if we try to feed our endpoint with below payload:

{
  "age": 16
}
Enter fullscreen mode Exit fullscreen mode

It would cause below error:

{
    "statusCode": 400,
    "message": [
        "Age limit violation! Employee age should be between 18 and 65 years"
    ],
    "error": "Bad Request"
}
Enter fullscreen mode Exit fullscreen mode

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`;
  }
}
Enter fullscreen mode Exit fullscreen mode

And if we feed the api with invalid age value

{
  "age": 16
}
Enter fullscreen mode Exit fullscreen mode

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"
}
Enter fullscreen mode Exit fullscreen mode

And we are done! Hope you guys enjoyed it.

Adios
Stay tuned, Thank You!

Top comments (0)