Sling Academy
Home/Node.js/NestJS: How to Validate Nested Objects

NestJS: How to Validate Nested Objects

Last updated: January 01, 2024

Introduction

Validating incoming data is crucial for any backend application. In NestJS, nested object validation can ensure the integrity and structure of the data your app handles. This tutorial will explore how to apply validation rules seamlessly in your NestJS projects.

Setting Up Validation

Before diving into nested object validation, it’s imperative to set up the foundational validation package. NestJS leans on the class-validator package to enable decorators for validation rules. To begin, install the required packages with:

npm install class-validator class-transformer

Once installed, you’ll also need to integrate them into your module using ValidationPipe. Include the pipe globally in your main application file:

import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { ValidationPipe } from '@nestjs/common';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  app.useGlobalPipes(new ValidationPipe());
  await app.listen(3000);
}
bootstrap();

Basic Validation with DTO Classes

Data Transfer Object (DTO) classes are a primary way to define and validate the structure of the data in a NestJS app. For a simple object with a single level of properties, use class-validator decorators to describe the constraints:

import { IsString, IsInt, Min, Max } from 'class-validator';

class CreateUserDto {
  @IsString()
  readonly name: string;

  @IsInt()
  @Min(0)
  @Max(100)
  readonly age: number;
}

This DTO ensures that name is a string, and age is an integer between 0 and 100.

Validating Nested Objects

Let’s consider a case with nested objects:

import { IsString, IsInt, Min, Max, ValidateNested, IsOptional, Type } from 'class-validator';
class AddressDto {
  @IsString()
  readonly street: string;

  @IsInt()
  readonly zipCode: number;
}

class CreateUserDto {
  @IsString()
  readonly name: string;

  @IsInt()
  @Min(0)
  @Max(100)
  readonly age: number;

  @Type(() => AddressDto)
  @ValidateNested()
  @IsOptional()
  readonly address?: AddressDto;
}

In the above example, Type and ValidateNested decorators are used to handle nested object validation. The Type decorator indicates which class to transform the nested object into, while ValidateNested enforces the validation rules defined inside the nested DTO class.

Advanced Validation Techniques

For more complex scenarios, you can employ custom validators or use conditional validation using IsNotEmpty, IsOptional, and arrow functions to apply validation criteria flexibly.

Custom Validators

Here’s an example of creating a custom validator:

import { ValidatorConstraint, ValidatorConstraintInterface, ValidationArguments } from 'class-validator';

@ValidatorConstraint({ name: 'customTextLength', async: false })
class CustomTextLengthValidator implements ValidatorConstraintInterface {
  validate(text: string, args: ValidationArguments) {
    return text.length > 10 && text.length < 20; // custom validation criteria
  }
  defaultMessage(args: ValidationArguments) {
    return 'Text length is wrong';
  }
}

Decorate the property in your DTO with the custom validator:

class CreateUserDto {
  //... other properties

  @Validate(CustomTextLengthValidator)
  readonly bio: string;
}

Conditional Validation

Use conditions to apply validation rules only when certain conditions are met:

import { IsString, ValidateIf } from 'class-validator';
class CreateUserDto {
  //... other properties

  @IsString()
  @ValidateIf(o => o.bio !== '')
  readonly nickname: string;
}

This ensures that nickname must meet the IsString validation rule only if bio is not empty.

Groups and Nested Validation

Groups allow you to control which validation rules apply at different times. They are handy for cases when specific fields must be validated only in certain contexts, like during creation or update operations.

Define groups in your DTO:

import { IsString, IsInt, Min, Max, ValidateNested, GroupSequenceProvider, GroupSequence } from 'class-validator';

@GroupSequence(['CreateUserDto', 'FullCheck'])
class CreateUserDto {
  //... other properties
  @IsString({ groups: ['FullCheck'] })
  readonly detailedBio: string;
}

Then specify groups when using the validation pipe:

//... setup code

app.useGlobalPipes(new ValidationPipe({ groups: ['FullCheck'] }));

Custom Transformers

Apart from using built-in decorators from class-transformer, you can also define custom transformers to preprocess your data before validation occurs.

Conclusion

Validating nested objects in NestJS using class-validator ensures structured and predictable data throughout your application. By utilizing the validation decorators and techniques we’ve covered, you can create robust and secure NestJS applications tailored to your data requirements. Embrace these features to keep your application’s input solid and your server operations reliable.

Next Article: NestJS: How to Send Custom Headers with Response

Previous Article: NestJS: How to Validate Request Body with Joi

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