DEV Community

Abdul Ghofur
Abdul Ghofur

Posted on • Edited on

2

zod vs class-validator & class-transformer

Comparison of zod with class-validator & class-transformer in NestJS

I was confused, or at least curious between zod or class-validator & class-transformer
as a validation library in NestJS.

Bahasa Indonesia Version

https://abdulghofurme.github.io/posts/zod-vs-class-validator-n-class-transformer

Main Point

Just go straight to it.

1. Reason for choosing class-validator & class-transformer

  • Is a duo package that is common & widely used in NestJS
  • The writing method is "very NestJS", because it is decorator-based validation
  • clean & seamless integration with its use with class-transformer & ValidationPipe

2. Reason for choosing zod

  • Framework agnostic
  • Very Typescript
  • For those who prefer functional & schema-based approach
  • Performance & lightweight validation is critical

Detail

class-validator & class-transformer are the 2 packages most commonly used as validation in NestJS,
yes, apart from the fact that the writing method is the same as NestJS using decorator-based,
also because it is clean & seamless because it can be used with ValidationPipe as DTO.

So, the incoming data/payload received by the controller has been validated and changed/transformed according to its definition.
Meanwhile zod still needs to validate the data/payload it receives manually,
yes, maybe only 1 or a maximum of 3 lines,
but of course, the more validation functions are needed the more manual processes are required.

Procedure details

Here is a detailed procedure (perhaps subjective) for comparison.

class-validator & class-transformer

1. Installation

npm install class-validator class-transformer
Enter fullscreen mode Exit fullscreen mode

2. Enable Global Validation

// main.ts
....
import { ValidationPipe } from '@nestjs/common';

async function bootstrap() {
....
  // Enable validation globally
  app.useGlobalPipes(new ValidationPipe({
    transform: true, // Automatically transform payloads to DTO instances
    whitelist: true, // Strip unknown properties
    forbidNonWhitelisted: true, // Throw error for unknown properties
  }));
....
}
....
Enter fullscreen mode Exit fullscreen mode

3. Define DTO(Data Transfer Object)

import { IsNotEmpty, IsString, IsInt, Min } from "class-validator";
import { Type, Transform } from "class-transformer";

export class CreateUserDto {
    @Transform(({ value }) => value.trim()) // Trim whitespaces
    @IsNotEmpty({ message: "Nama tidak boleh kosong" })
    @IsString({ message: "Nama harus berupa string" })
    @Min(3, { message: "Minimal panjang nama 3 karakter" })
    name: string;

    @Type(() => Number) // Transform input into Number type
    @IsNotEmpty({ message: "Nama tidak boleh kosong" })
    @IsInt({ message: "Umur harus berupa bilangan bulat" })
    @Min(17, { message: "Minimal umur terdaftar 17 tahun" })
    age: number;
}
Enter fullscreen mode Exit fullscreen mode

Quite long, yes, but it's decorator-based.

4. Penggunaan validasi

import { Body, Controller, Post } from "@nestjs/common";
import { CreateUserDto } from "./create-user.dto";

@Controller("users")
export class UsersController {
    @Post()
    create(@Body() createUserDto: CreateUserDto) {
        // At this point data/payload createUserdDto
        // has been validated & changed according to the definition
        // developers can directly execute the service
        // or other logic
    }
}
Enter fullscreen mode Exit fullscreen mode

zod

1. Installation

npm install zod
Enter fullscreen mode Exit fullscreen mode

2. Create Validation Schema

// user.validaiton.ts
import { z, ZodType } from "zod";

export class UserValidation {
    static readonly CREATE: ZodType = z.object({
        name: z
            .string({ message: "Nama harus berupa string" })
            .nonempty({ message: "Nama tidak boleh kosong" })
            .min(3, "Minimal panjang nama 13 karakter"),
        age: z
            .number({ message: "Umur harus berupa angka" })
            .int({ message: "Umur harus berupa bilangan bulat" })
            .min(17, "Minimal umur terdaftar 17 tahub"),
    });
}

export type TCreateUserPayload = z.infer<typeof UserValidation.CREATE>;
Enter fullscreen mode Exit fullscreen mode

*Personal: I prefer reading this schema compared to the one above

3. Validation Implementations

import { Body, Controller, Post } from "@nestjs/common";
import { UserValidation, TCreateUserPayload } from "./user.validation.ts";

@Controller("users")
export class UsersController {
    @Post()
    create(@Body() createUserPayload: TCreateUserPayload) {
        const payload = UserValidation.CREATE.parse(createUserPayload);
        // At this point data/payload payload
        // has been validated & changed according to the definition
        // developers can directly execute the service
        // or other logic
    }
}
Enter fullscreen mode Exit fullscreen mode

Conclusion

Personally, I prefer the use of zod.
However, what needs to be underlined is choose according to the team's needs & standards.

Heroku

Build apps, not infrastructure.

Dealing with servers, hardware, and infrastructure can take up your valuable time. Discover the benefits of Heroku, the PaaS of choice for developers since 2007.

Visit Site

Top comments (1)

Collapse
 
programmerraja profile image
Boopathi

This is a good comparison of Zod and class-validator & class-transformer.
I think Zod's schema-based approach is very appealing for clean validation logic.

Image of Docusign

🛠️ Bring your solution into Docusign. Reach over 1.6M customers.

Docusign is now extensible. Overcome challenges with disconnected products and inaccessible data by bringing your solutions into Docusign and publishing to 1.6M customers in the App Center.

Learn more

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay