Lesson 16-NestJS Serialization

In NestJS, serialization refers to the process of converting an object into a format suitable for storage or transmission. This typically involves transforming an object’s state to ensure safe transfer over a network or persistent storage. While NestJS does not provide built-in serialization functionality, you can achieve serialization and validation using third-party libraries like class-transformer and class-validator.

Installing Dependencies

First, install class-transformer and class-validator:

npm install class-transformer class-validator

Creating DTO Class

A DTO (Data Transfer Object) class is used to represent data transfer objects, typically for serialization and deserialization. Below is an example of a simple DTO class for user data.

// src/users/dto/user.dto.ts
import { ApiProperty } from '@nestjs/swagger';
import { Expose } from 'class-transformer';
import { IsEmail, IsString, IsNotEmpty } from 'class-validator';

export class UserDto {
  @ApiProperty()
  @Expose()
  @IsString()
  @IsNotEmpty()
  public id: string;

  @ApiProperty()
  @Expose()
  @IsString()
  @IsNotEmpty()
  public firstName: string;

  @ApiProperty()
  @Expose()
  @IsString()
  @IsNotEmpty()
  public lastName: string;

  @ApiProperty()
  @Expose()
  @IsEmail()
  @IsNotEmpty()
  public email: string;

  // This property will not be serialized
  @ApiProperty({ readOnly: true })
  @IsString()
  @IsNotEmpty()
  private _password: string;

  // Getter method to expose password
  get password(): string {
    return this._password;
  }

  // Setter method to set password
  set password(value: string) {
    this._password = value;
  }
}

In this example, the @Expose() decorator specifies which properties should be serialized. The _password property is private and accessed via getter and setter methods, allowing control over when and how the password is exposed.

Serialization and Deserialization

Next, create a service to handle serialization and deserialization processes.

// src/users/users.service.ts
import { Injectable } from '@nestjs/common';
import { UserDto } from './dto/user.dto';

@Injectable()
export class UsersService {
  async createUser(userDto: UserDto): Promise<UserDto> {
    // Add business logic here, e.g., saving to a database
    return userDto;
  }

  async getUser(id: string): Promise<UserDto> {
    // Add business logic here, e.g., fetching from a database
    const user = new UserDto();
    user.id = id;
    user.firstName = 'John';
    user.lastName = 'Doe';
    user.email = 'john.doe@example.com';
    user.password = 'secret'; // Password will not be serialized
    return user;
  }
}

Serialization in Controllers

In a controller, use the DTO class to receive and return data.

// src/users/users.controller.ts
import { Controller, Post, Body, Get, Param } from '@nestjs/common';
import { UsersService } from './users.service';
import { UserDto } from './dto/user.dto';

@Controller('users')
export class UsersController {
  constructor(private readonly userService: UsersService) {}

  @Post()
  async createUser(@Body() userDto: UserDto): Promise<UserDto> {
    return this.userService.createUser(userDto);
  }

  @Get(':id')
  async getUser(@Param('id') id: string): Promise<UserDto> {
    return this.userService.getUser(id);
  }
}

Testing Serialization

To test serialization, use tools like Postman to send requests to the API.

Create User
Send a POST request to the /users endpoint with user data:

{
  "firstName": "John",
  "lastName": "Doe",
  "email": "john.doe@example.com",
  "password": "secret"
}

Get User
Send a GET request to /users/:id, e.g., /users/1. The response should exclude the password information.

Excluding Specific Properties

To completely exclude certain properties from serialization, use the @Exclude() decorator:

// src/users/dto/user.dto.ts
import { Exclude } from 'class-transformer';

// ...

export class UserDto {
  // ...

  @Exclude()
  private _password: string;

  // ...
}

Explicit Conversion

For explicit conversion during serialization, use the plainToClass and classToPlain methods provided by class-transformer:

// src/users/users.service.ts
import { plainToClass } from 'class-transformer';

// ...

@Injectable()
export class UsersService {
  async createUser(userDto: UserDto): Promise<UserDto> {
    // Add business logic here, e.g., saving to a database
    return userDto;
  }

  async getUser(id: string): Promise<UserDto> {
    const plainUser = {
      id: id,
      firstName: 'John',
      lastName: 'Doe',
      email: 'john.doe@example.com',
      password: 'secret', // Password will not be serialized
    };
    return plainToClass(UserDto, plainUser);
  }
}
Share your love