Video

Watch the full video on YouTube:

POST, Body and DTO Validation

Introduction

In this seventh installment of the NestJS guide, we’ll implement the POST method to receive enriched data through the body and learn to validate it using DTOs with the class-validator library.

POST Method and @Body()

The POST method allows us to receive data through the body, which is a string usually in JSON format. This gives us flexibility to send data in enriched formats like arrays, nested objects, and long definitions.

Creating a POST Endpoint

@Post('pets')
savePet(@Body() input: unknown) {
  return input
}

The @Body() decorator allows us to access the content sent in the request body.

Introduction to DTOs

DTO (Data Transfer Object) is an object that allows us to transform JSON format data into a class we can use to manipulate or validate data.

Creating a DTO

export class SavePetBodyDto {
  name: string
}

Validation with class-validator

NestJS offers an alternative to apply validations in a sustainable way using class-validator and class-transformer.

Installation

pnpm add class-validator class-transformer

Applying Validation Decorators

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

export class SavePetBodyDto {
  @IsNotEmpty()
  @IsString()
  name: string
}

Using ValidationPipe

For validations to be applied, we must add the ValidationPipe in our controller:

@Post('pets')
savePet(@Body(new ValidationPipe()) input: SavePetBodyDto) {
  return input
}

Common Validations

String Length

import { IsNotEmpty, IsString, MinLength, MaxLength } from 'class-validator'

export class SavePetBodyDto {
  @IsNotEmpty()
  @IsString()
  @MinLength(2)
  @MaxLength(7)
  name: string
}

Email Validation

import { IsEmail, IsNotEmpty, IsString } from 'class-validator'

export class SavePetBodyDto {
  @IsNotEmpty()
  @IsString()
  name: string

  @IsEmail()
  email: string
}

Owner Validation

export class SavePetBodyDto {
  @IsNotEmpty()
  @IsString()
  name: string

  @IsEmail()
  email: string

  @IsNotEmpty()
  @IsString()
  owner: string
}

Query String Validation

We can also validate query strings using DTOs:

Creating a Query DTO

export class ValidateQueryDto {
  @IsNotEmpty()
  @IsString()
  @MinLength(2)
  name: string
}

Applying in Controller

@Get('pets')
findAll(@Query(new ValidationPipe()) query: ValidateQueryDto) {
  return query
}

It’s good practice to separate DTOs in a dedicated folder:

src/
├── dto/
│   ├── save-pet-input.dto.ts
│   └── validate-query.dto.ts
├── controllers/
└── services/

Example DTO in Separate File

// src/dto/save-pet-input.dto.ts
import { IsEmail, IsNotEmpty, IsString, MinLength, MaxLength } from 'class-validator'

export class SavePetBodyDto {
  @IsNotEmpty()
  @IsString()
  @MinLength(2)
  @MaxLength(7)
  name: string

  @IsEmail()
  email: string

  @IsNotEmpty()
  @IsString()
  owner: string
}

NestJS 12

NestJS recently updated to version 12, which includes:

  • Framework modernization with ESM modules
  • New standard validation layer with libraries like Zod and Valibot
  • Unit testing support with Vitest (in addition to Jest)
  • Documentation and homepage redesign

Conclusion

The POST method is fundamental for receiving enriched data in our APIs. Combined with DTOs and class-validator, we can separate validation logic from business logic in a clean and maintainable way, applying the single responsibility principle.

TypeScript Guide 06: Function Typing