How to serialize responses in nestjs
Tags: nestjs, node, typescript, javascript
Published: 2023-05-01 11:25:50

---
title: How to serialize responses in nestjs
published: true
date: 2023-05-01 08:56:30 UTC
tags: #nestjs #nodejs #typescript #javascript
canonical_url: https://salwoch.hashnode.dev/how-to-serialize-responses-in-nestjs
---

When writing APIs, you'll most likely have to return responses to the client. For example, when writing a sign-up API, you may be required to return the created user object to the client.

An important step to take in development is ensuring that only the required amount of data is returned for performance reasons -and that It does not include sensitive information such as passwords that could compromise your users' accounts. This can be achieved by serializing responses before they are returned..

In this tutorial, you'll learn how serialization works in nestjs and how to serialize your responses in nestjs

## prerequisites

To follow along seamlessly, this article assumes you have:

- some experience building REST APIS with nodejs and nestjs

- An understanding of nestjs project structure and terminologies such as decorators, modules, controllers, providers etc

- Some experience programming in javascript and typescript

## How nestjs serialization works

serialization in nestjs is enabled using the `ClassSerializerInterceptor.` It intercepts the responses and serialises them to the desired format before returning them to the client. The `ClassSerializerInterceptor` interceptor can be set at the application level to enable serialization by controllers -or at the service level for serialization by services.

An entity class (serializer class) is created to define the shape of the data one would like to return to the client. The [class transformer](https://github.com/typestack/class-transformer) package is then used to provide a set of rules to transform the data before it is returned to the client. The serialized data becomes an instance of the entity class and the returned value of a method handler as shown in the example below.

## Example.

This example is a citizen registration app where citizens signup to become users by providing their information. It demonstrates serializing data by excluding some information provided such as `password` and includes the `citizenStatus` -a computation derived from the provided date of birth.

### Set up:

To get started, in your terminal, install [nestjs CLI](https://docs.nestjs.com/cli/overview) if you do not have it installed already

```
npm install -g @nestjs/cli

```

Use the nest CLI to generate a new project boilerplate and select the package manager you'll be using

```
nest new citizen_app

```

next, install some packages that will be required for this example

```
npm i --save class-validator class-transformer

```

In the root of your project, create a userData.json file to serve as data storage for this project

```
touch userData.json

```

### Rest Endpoints:

With the set-up complete, regenerate a resource for the user and select generate REST endpoints

```
nest g resource user

```

A new user folder should be added to your src folder containing, service, controllers, dto, entities etc

In the `create-user.dto.ts`, define the properties that will be used to create the user. Add some validations to the properties using the [class-validator](https://github.com/typestack/class-validator) package as shown below:

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

export class CreateUserDto {
  @IsNotEmpty() //should not be empty
  @IsString() //should be a string
  firstname: string;

  @IsNotEmpty()
  @IsString()
  lastname: string;

  @IsDateString({ strict: true }) //should be in format 2022-07-15.
  dateOfBirth: Date;

  @IsEmail() //should be a valid email
  email: string;

  @IsNotEmpty()
  @IsString()
  password: string;

```

You have to enable global validations in your `src/main.ts` `bootstrap()` function for the validations to be applied. Add this code just before your `app.listen()`

```
app.useGlobalPipes(new ValidationPipe())

```

In the `user` folder, create a new folder called `interfaces.` create an interface for the created user

```
export interface IUser {
  id: number;
  firstname: string;
  lastname: string;
  dateOfBirth: Date;
  email: string;
  password: string;
}

```

In the `src/user.service.ts`, the `UserService` class will need to have access to the data storage file to read and write to it. To achieve that, The `UserService` class has to instantiate by reading the `userData.json` file as follows:

```
import * as fs from 'fs';
import * as path from 'path';
import {Injectable} from '@nestjs/commom';
import { CreateUserDto } from './dto/create-user.dto';
import { IUser } from './interfaces';

@Injectable()
export class UserService {
  private userData: IUser[];

  constructor() {
    fs.promises
      .readFile(path.join(__dirname, '../../userData.json'), 'utf-8')
      .then((data) => {
        this.userData = JSON.parse(data);
      })
      .catch((err) => {
        console.error(err);
      });
  }

  private saveUser() {
    fs.promises.writeFile(
      path.join(__dirname, '../../userData.json'),
      JSON.stringify(this.userData),
    );
  }
//the rest of the generated methods go here...
}

```

Still in `src/user/user.service.ts`, add some actual implementation to the rest of the method handlers.

```
//continuation of userService class...
//create new user
  create(dto: CreateUserDto) {
    const existingUser = this.userData.find((user) => user.email === dto.email);
    if (existingUser) throw new ConflictException('user already exists');

    const newUser: IUser = {
      id: this.userData.length + 1,
      firstname: dto.firstname,
      lastname: dto.lastname,
      dateOfBirth: dto.dateOfBirth,
      email: dto.email,
      password: dto.password,
    };
    this.userData.push(newUser); //add new user to userData
    this.saveUser();

    return newUser;
  }

// find all users
  findAll() {
    return this.userData;
  }

// find a user with id
  findOne(id: number) {
    const user = this.userData.find((user) => user.id === id);
    if (!user) throw new NotFoundException('user not found');
    return user;
  }

// delete user
  remove(id: number) {
    const userIndex = this.userData.findIndex((user) => user.id === id);
    if (userIndex === -1) throw new NotFoundException('user not found');
    this.userData.splice(userIndex, 1);
    this.saveUser();
  }

```

Note that in a real application, you'll have to secure your passwords before saving them by hashing them using a third-party package such as [bcrypt](https://www.npmjs.com/package/bcryptjs)

With the `UserService` fully implemented, start the server by running `npm run start:dev` in your terminal and send some requests. You can use Postman, Insomnia or any other tool of your choice.

The response from making a `POST` request to the `user` endpoint should look like this in Postman.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1682917494451/c42c888c-8a9c-421c-b8dd-78cf09766cda.png)

Notice that among other properties returned in the response object, we have `password` -which would compromise the security of your users' data. The plan is to have the password excluded from the response object. For this example, the `dateOfBirth` property will also be excluded and instead, a `citizenStatus` property -indicating whether the user is a child, adult or senior depending on their date of birth will be included.

### Serializing the endpoints

The plan is to use the `ClassSerializerInterceptor` at the application level so that all the route handlers in the controller that are provided with an instance of the entity class (serializer class) can perform serialization.

In your `src/main.ts`, import the `ClassSerializerInterceptor` from `@nestjs/common` and add the interceptor globally. Your final `bootstrap()` function should look like this:

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

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  app.useGlobalPipes(new ValidationPipe()); //set validations to the application level
  // 👇 apply transform to all responses
  app.useGlobalInterceptors(new ClassSerializerInterceptor(app.get(Reflector)));
  await app.listen(3000);
}
bootstrap();

```

Next, create an entity class and supply a set of rules for transforming the data. Add this to your `src/user/entities/user.entity.ts`

```
import { Exclude, Expose } from 'class-transformer';
import { IUser } from '../interfaces';

interface IUserEntity extends IUser {
  get citizenStatus(): CitizenStatus;
}

type CitizenStatus = 'Child' | 'Adult' | 'Senior';

export class UserEntity implements IUserEntity {
  id: number;
  firstname: string;
  lastname: string;
  email: string;

  @Exclude()
  dateOfBirth: Date;

  @Exclude()
  password: string;

  @Expose()
  get citizenStatus(): CitizenStatus {
    const birthDate = new Date(this.dateOfBirth).getFullYear();
    const currentYear = new Date().getFullYear();
    const age = currentYear - birthDate;
    if (age < 18) {
      return 'Child';
    } else if (age > 18 && age < 60) {
      return 'Adult';
    } else {
      return 'Senior';
    }
  }

```

In the provided snippet, the `@Exclude()` decorator from the [class-transformer](https://github.com/typestack/class-transformer) package is used to indicate that the `password` and `dateOfBirth` properties should be excluded from the response objects. On the other hand, the `citizenStatus` property is indicated to be included using the `@Expose()` decorator.

Since `citizenStatus` is not an actual property of the entity class -but rather a computed value based on the `dateOfBirth` property, the `get` keyword is used to define a getter method for `citizenStatus` instead of a regular property.

With the entity class created, the next step is to use it in the controller. Refactor your `src/user/user.controller.ts`to match this:

```
import { Controller, Get, Post, Body, Param, Delete } from '@nestjs/common';
import { UserService } from './user.service';
import { CreateUserDto } from './dto/create-user.dto';
import { UserEntity } from './entities/user.entity';

@Controller('user')
export class UserController {
  constructor(private readonly userService: UserService) {}

  @Post()
  create(@Body() createUserDto: CreateUserDto): UserEntity {
    return new UserEntity(this.userService.create(createUserDto));
  }

  @Get()
  findAll(): UserEntity[] {
    const users = this.userService.findAll();
    return users.map((user) => new UserEntity(user));
  }

  @Get(':id')
  findOne(@Param('id') id: string): UserEntity {
    return new UserEntity(this.userService.findOne(+id));
  }

  @Delete(':id')
  remove(@Param('id') id: string) {
    return this.userService.remove(+id);
  }
}

```

The controller's method handlers that will need to be serialized are refactored to return an instance of the entity class with the return values of the delegated service handlers. A return type of `UserEntity` is also added.

Restart your application and send some requests to confirm the serialiser is working as expected. The `GET` request to fetch all users provides the following response:

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1682922286821/637c4859-23c5-4e98-828e-d1033a8fb0a8.png)

From the response, you can tell the data has been properly serialized to the desired format.

## Conclusion

If you've made it to the end of this article, you've learnt how serialization works in nestjs and learnt hands-on to serialize data in nestjs. From the example, you can tell that among other things, you can return only a subset of an object and also make use of getters and setters.

There are so many other transformations you can apply to your response objects and this tutorial only scratched the surface with those. To dive deep into transformations, feel free to read more on the [class-transformer Github](https://github.com/typestack/class-transformer). Also, check out the [nestjs docs](https://docs.nestjs.com/techniques/serialization) on this same topic.

The complete source code for this example can be found on Github [here](https://github.com/Alwoch/nestjs_serialization)