Sling Academy
Home/Node.js/NestJS: Validate request body based on one property

NestJS: Validate request body based on one property

Last updated: December 31, 2023

Learn to validate a request body conditionally based on a property value in NestJS, enhancing your application’s robustness and data integrity.

Introduction to Validation in NestJS

In NestJS, request validation is an essential part of creating robust and secure applications. It ensures that incoming data meets the expected format and value criteria before processing it. NestJS leverages class-validator and class-transformer libraries to provide declarative validation that aligns seamlessly with the framework’s module-based architecture. In this tutorial, we will dive deep into conditional validation, focusing on how to trigger validations based on specific property values within a request body.

Basic Conditional Validation

class-validator offers a straightforward way to perform object-level property validation. The first step is to define your validation rules within a DTO (Data Transfer Object). Here’s an example:

import { IsString, IsNotEmpty, ValidateIf } from 'class-validator';

class MyAppRequestDTO {
  @IsNotEmpty()
  @IsString()
  action: string;

  @ValidateIf(o => o.action === 'create')
  @IsNotEmpty()
  payload: any;
}

With the above code, the `payload` property will only be checked for being not empty if the `action` property equals ‘create’.

Creating Custom Decorators for Validation

When predefined decorators are not sufficient, you can create custom decorators in NestJS. Here’s a simple custom decorator that checks if a property exists based on another property’s value:

import { registerDecorator, ValidationOptions, ValidationArguments } from 'class-validator';

function IsPropertyConditional(property: string, validationOptions?: ValidationOptions) {
  return function (object: Object, propertyName: string) {
    registerDecorator({
      name: 'isPropertyConditional',
      target: object.constructor,
      propertyName: propertyName,
      constraints: [property],
      options: validationOptions,
      validator: {
        validate(value: any, args: ValidationArguments) {
          let relatedValue = args.object[args.constraints[0]];
          return relatedValue ? value !== null && value !== undefined : true;
        }
      }
    });
  };
}

This decorator will ensure that the marked property exists only when the specified condition is met.

Advanced Conditional Validation Scenarios

For more complex validation scenarios, you might need to perform an asynchronous operation or access a service within your custom validation decorator. Imagine needing to check if a username is unique only when a user decides to change it. Such a process would involve reaching out to the database, which can be asynchronous:

import { Injectable } from '@nestjs/common';
import { ValidatorConstraint, ValidatorConstraintInterface, ValidationArguments } from 'class-validator';

class UniqueUsernameValidator implements ValidatorConstraintInterface {
  constructor(private readonly userService: UserService) {}

  async validate(username: string, args: ValidationArguments) {
    if (args.object['changeUsername'] === true) {
      return await this.userService.isUsernameUnique(username);
    }
    return true;
  }
}

Incorporating your custom logic into decorators like this allows for rich, real-world data validation flows including database integration. However, managing dependencies for your custom validators requires setting up provider injections, which brings additional complexity and the need to configure the global module in NestJS.

Putting It All Together

Once you’ve established your validation rules, integrate them with NestJS’s `Pipe` mechanism. Apply the `ValidationPipe` to your route handlers to automatically enforce the rules:

import { Controller, Post, Body, UsePipes, ValidationPipe } from '@nestjs/common';

@Controller('my-app')
export class MyAppController {
  constructor(private readonly myAppService: MyAppService) {}

  @Post()
  @UsePipes(new ValidationPipe())
  async handleAction(@Body() requestDto: MyAppRequestDTO) {
    //... perform your business logic
  }
}

When a request is made to your endpoint, the `ValidationPipe` will automatically enforce the conditional validation rules you’ve defined in your DTO.

Summary

NestJS’s advanced validation capabilities offer a powerful way to ensure data integrity and protect your application from invalid data. Mastering conditional validation helps in covering a wide array of user interactions with confidence, providing flexible and bulletproof data validation strategies. Follow this tutorial’s concepts to boost the reliability of your server-side validations significantly.

Next Article: Solving NestJS Import Modules Error: Cannot Find Module

Previous Article: How to Automatically Send Emails in NestJS

Series: Nest.js Tutorials: From Basics to Advanced

Node.js

You May Also Like

  • NestJS: How to create cursor-based pagination (2 examples)
  • Cursor-Based Pagination in SequelizeJS: Practical Examples
  • MongooseJS: Cursor-Based Pagination Examples
  • Node.js: How to get location from IP address (3 approaches)
  • SequelizeJS: How to reset auto-increment ID after deleting records
  • SequelizeJS: Grouping Results by Multiple Columns
  • NestJS: Using Faker.js to populate database (for testing)
  • NodeJS: Search and download images by keyword from Unsplash API
  • NestJS: Generate N random users using Faker.js
  • Sequelize Upsert: How to insert or update a record in one query
  • NodeJS: Declaring types when using dotenv with TypeScript
  • Using ExpressJS and Multer with TypeScript
  • NodeJS: Link to static assets (JS, CSS) in Pug templates
  • NodeJS: How to use mixins in Pug templates
  • NodeJS: Displaying images and links in Pug templates
  • ExpressJS + Pug: How to use loops to render array data
  • ExpressJS: Using MORGAN to Log HTTP Requests
  • NodeJS: Using express-fileupload to simply upload files
  • ExpressJS: How to render JSON in Pug templates