Serialization in NestJS: A different approach
Tags: nestjs, node, webdev
Published: 2020-02-19 16:45:34

---
title: Serialization in NestJS: A different approach
published: true
date: 2020-02-17 14:00:37 UTC
tags: nestjs, node, webdev
canonical_url: https://blog.logrocket.com/serialization-in-nestjs-a-different-approach/
cover_image: https://dev-to-uploads.s3.amazonaws.com/i/5s6fizl3uqrp6wp044di.jpeg
---
**Written by [Maciej Cieślar](https://blog.logrocket.com/author/maciejcieslar/)**✏️

When we deal with objects in our applications, they often contain sensitive information. An object that represents a user fetched from the database, for example, would likely contain a password

Other times, a lot of information saved in an object is not useful for the end client and should be removed before sending the object over the network to save bandwidth. The process that takes care of both of these is called serialization.

Serialization is a process of preparing an object to be sent over the network to the end client. To prepare an object could be to exclude some of its sensitive or unnecessary properties or to add some additional ones.

NestJS provides a way to [serialize objects](https://docs.nestjs.com/techniques/serialization) returned from our API endpoints with the use of a little decorator magic and a library called class-transformer. The solution is good for very basic cases, but falls short in more complicated ones.

For example, to serialize an object, we have to return the object itself. While it may not seem like a problem, consider the following:

```jsx
@UseInterceptors(ClassSerializerInterceptor)
@Get()
findOne(): UserEntity {
  return new UserEntity({
    id: 1,
    firstName: 'Kamil',
    lastName: 'Mysliwiec',
    password: 'password',
  });
}
```

The example was taken from the official Nest documentation.

As expected, the object returned by the `findOne` method will be serialized. Now, what if we wanted to create a `findAll` method that returns many user objects? Usually such endpoints return more than just the entities — say, pagination info.

```jsx
@UseInterceptors(ClassSerializerInterceptor)
@Get()
findMany(): UserEntity {
  const users = [user1, user2];

  return {
    users,
    ...paginationInfo,
  }
}
```

The users property won’t be serialized… Why would it? It is clearly stated in the documentation:

> **Note:** we must return an instance of the class. If you return a plain JavaScript object — for example, `{ user: new UserEntity() }` — the object won’t be properly serialized.

And thus, the pseudo-code above illustrates the lack of flexibility that the out-of-the-box serialization provides.

Thankfully, creating a reusable serialization solution with the use of the Nest ecosystem is a breeze. Let’s get right to it.

[![LogRocket Free Trial Banner](https://i0.wp.com/blog.logrocket.com/wp-content/uploads/2017/03/f760c-1gpjapknnuyhu8esa3z0jga.png?resize=1200%2C280&ssl=1)](https://logrocket.com/signup/)

## Creating our own serialization mechanism

In order to create our own serialization, there are two things we have to implement.

First, we have to create a “parent” class that every serializer will extend. There, we’ll put all the reusable methods, such as serialize or `serializeCollection`. Second, as in the example above, we have to create our own interceptor that will take care of actually running our serializers.

Serialization often includes checking users’ roles to see what kind of properties of a given object are they allowed to retrieve.

Aside from excluding the unwanted values from objects, our serialization will also provide other features, such as asynchronous serialization, nested serialization, and adding additional properties that weren’t in the original object.

Here’s the flow of our serialization.

Each controller marks which properties should be serialized like:

```jsx
return {
  user: this.userSerializerService.markSerializableValue(user),
  otherProperty: true,
};
```

Then, the interceptor goes over the keys of the returned object and serializes the values that were marked. To “mark” an object, we simply wrap it into a class called `Serializable`. This way, from inside the interceptor, we can easily check whether a value of the property is an instance of the `Serializable` class.

```jsx
export class Serializable<T> {
 public constructor(public readonly serialize: () => Promise<T | T[]>) {}
}
```

The class doesn’t do anything itself aside from keeping a reference to a function that will be used to serialize a value. The function will be provided by the serializer.

So the final shape of the object above would be:

```jsx
return {
  user: Serializable<User>,
  otherProperty: true,
};
```

With this in mind, let’s create the base serializer.

## `BaseSerializerService`

We are going to create an abstract class called `BaseSerializerService` that will provide all the reusable methods for all the serializers.

```jsx
export abstract class BaseSerializerService<E, T> {
  // ...
}
```

The class takes two generic types, `E` and `T`, which stand for an entity and a serialized value, respectively.

### Serialization methods

```jsx
 public abstract async serialize(entity: E, role: UserRole): Promise<T>;

 private serializeCollection(values: E[], role: UserRole): Promise<T[]> {
   return Promise.all<T>(values.map((v) => this.serialize(v, role)));
 }
```

Each serializer will implement its own `serialize` method; thus, the method is `abstract` and has no implementation. The `serialize` method takes an entity and a user role. Then, taking into account the user role, it serializes the entity. Afterwards, the serialized object is ready to be sent to the end client.

The second method is called `serializeCollection`, which takes an array of entities and returns an array of serialized objects.

Admittedly, we could have used a single method called `serialize` and checked ourselves whether the provided value is an array, but it is better to keep the API as unambiguous as possible.

### Wrapping values

In order to mark the returned value as serializable so the interceptor could serialize it later, we provide two methods:

```jsx
 public markSerializableValue(value: E): Serializable<T> {
   return new Serializable<T>(this.serialize.bind(this, value));
 }

 public markSerializableCollection(values: E[]): Serializable<T[]> {
   return new Serializable<T[]>(this.serializeCollection.bind(this, values));
 }
```

Both functions accept one parameter: in the first case, it is an entity, and in the second, a collection of entities.

With the serializers methods in place, we simply pass them to the `Serializable` class so they can be called later by the interceptor. Keep in mind that the serialization doesn’t happen until the interceptor calls the provided functions.

Once again, the `Serializable` class doesn’t do anything except for keeping a reference to the provided function so that it can be used later inside the interceptor.

## `SerializerInterceptor`

Interceptors in Nest are called before and after a request is handled, providing us with the opportunity to transform the object returned from a controller method.

```jsx
export interface AuthenticatedRequest extends Request {
 readonly user: User;
}

@Injectable()
export class SerializerInterceptor implements NestInterceptor {
 private async serializeResponse(
   response: Response,
   role: UserRole,
 ): Promise<Record<string, any>> {
   const serializedProperties = await Promise.all(
     Object.keys(response).map(async (key) => {
       const value = response[key];

       if (!(value instanceof Serializable)) {
         return {
           key,
           value,
         };
       }

       const serializedValue = await value.serialize(role);

       return {
         key,
         value: serializedValue,
       };
     }),
   );

   return serializedProperties.reduce((result, { key, value }) => {
     result[key] = value;

     return result;
   }, {});
 }

 public intercept(
   context: ExecutionContext,
   next: CallHandler,
 ): Observable<any> {
   const request = context.switchToHttp().getRequest<AuthenticatedRequest>();

   return next.handle().pipe(
     switchMap((response) => {
       if (typeof response !== 'object' || response === null) {
         return of(response);
       }

       return from(this.serializeResponse(response, request.user?.role));
     }),
   );
 }
}
```

The public method `intercept` is required by Nest, and it is called before each request. It has two parameters: `context` and `next`.

Thanks to the `context` object, we can easily get access to the underlying `http` request.

We are going to assume that there are some guards or middleware that set the authenticated user object in `request.user` property.

Having access to the `user` object, we can easily get the role of the authenticated user. Just to be safe, we are using the optional chaining operator `?` introduced recently in TypeScript in case the user object has not been set.

The `next` object has a `.handle()` method that resumes the processing of the request. If we decided that a request wasn’t supposed to be handled, we could have ended the execution early and returned an empty observable instead of calling `next.handle()`.

The `next.handle()` method returns an observable that at one point in time will emit the response. We are using RxJS’ `switchMap` operator, which ensures that only one response is returned. There are cases in which this would not be the expected behavior — for example, if the interceptor was used with WebSockets.

Inside the function we provided to the `switchMap` operator, we check whether the response

is even an object in the first place, because if it isn’t, then there’s nothing to serialize. Note that instead of returning the `response` itself, we have to wrap it in an observable using the `of` function since `switchMap` expects us to return an observable.

If the `response` is indeed an object, we are going to use the `serializeResponse` method. Since we support asynchronous serialization, we are wrapping the returned promise in a `from` function to create an observable from the promise.

Let’s take a look at the `serializeResponse` method.

```jsx
 private async serializeResponse(
   response: Response,
   role: UserRole,
 ): Promise<Record<string, any>> {
   const serializedProperties = await Promise.all(
     Object.keys(response).map(async (key) => {
       const value = response[key];

       if (!(value instanceof Serializable)) {
         return {
           key,
           value,
         };
       }

       const serializedValue = await value.serialize(role);

       return {
         key,
         value: serializedValue,
       };
     }),
   );
```

Knowing that the response provided as an argument is an object, we can safely use the `Object.keys` method to iterate over the object’s keys.

The method can be split into two parts: serializing the properties and forming the response object.

First, we map over the keys and check whether their respective value is an instance of `Serializable`. If it is, we execute the `serialize` method and await its result, returning it as the new value. Otherwise, we just return the existing value.

We have to wrap the mapping in the `Promise.all` method to make sure every promise is resolved before continuing. As a result, after the process takes place, we are left with an array of objects with the following shape: `{ key, value }`. Thanks to the usage of `Promise.all`, we are able to run the serializations of many properties simultaneously.

Next, we reduce the array of objects and values into an object, returning the object with the exact shape like the original one, but with all the properties serialized.

## Real-world use case

Assume that there’s a user entity that represents an author. It can reference many articles that the author wrote. Here’s what the entity would look like with the TypeORM library:

```jsx
@Entity(USER_TABLE_NAME)
export class User {
 @PrimaryGeneratedColumn('uuid')
 public id: string;

 @Column('text', { unique: true })
 public email: string;

 @Column('text')
 public password: string;

 @Column({ type: 'enum', enum: UserRole })
 public role: UserRole;

 @OneToMany(
   () => Article,
   (article) => article.author,
 )
 public articles: Article[];
}
```

Our goal in serialization would be to make sure that the `password` property is removed and the nested articles are also serialized. In order to keep the code clean and reusable, it would be best to use the `articleSerializerService` to serialize an article instead of writing the same logic in the `userSerializerService`.

```jsx
@Injectable()
export class UserSerializatorService extends BaseSerializerService<
 User,
 SerializedUserDTO
> {
 public constructor(
   private readonly articleSerializatorService: ArticleSerializatorService,
 ) {
   super();
 }

 public async serialize(
   entity: User,
   role: UserRole,
 ): Promise<SerializedUserDTO> {
   const strippedEntity = _.omit(entity, [
     'password',
   ]);
   const articles = await this.articleSerializatorService.serializeCollectionForRole(
     entity.charters,
     role,
   );

   return {
     ...strippedEntity,
     articles,
   };
 }
}
```
```jsx
@UseInterceptors(SerializerInterceptor)
@Controller(USER_ENDPOINT)
export class UserController {
 public constructor(
   private readonly userSerializatorService: UserSerializatorService,
 ) {}
 @Get(USER_ID_ROUTE)
 public async get(
   @Param(USER_ID_PARAM) userId: string,
 ): Promise<GetUserResDTO> {
   const user = await this.userService.findOne({ userId })

   return {
     user: this.userSerializatorService.markSerializableValue(user),
   };
 }
}
```

We also have access to the requesting user’s role (passed from the interceptor), so we can strip some properties based on that.

The above example with all the code is included in the repository, this was just a preview — a glimpse into the possibilities of the solution we have just implemented.

## Summary

The default serialization provided in the Nest core is easy to set up, yet very simple. By implementing our own mechanism, we have gained a lot of flexibility and control over how the process is executed. The implementation is reusable and, with a few changes, can be quickly adapted to any existing or freshly created project.

* * * 
## Plug: [LogRocket](https://logrocket.com/signup/), a DVR for web apps
 
![LogRocket Dashboard Free Trial Banner](https://i2.wp.com/blog.logrocket.com/wp-content/uploads/2017/03/1d0cd-1s_rmyo6nbrasp-xtvbaxfg.png?resize=1200%2C677&ssl=1)
 
[LogRocket](https://logrocket.com/signup/) is a frontend logging tool that lets you replay problems as if they happened in your own browser. Instead of guessing why errors happen, or asking users for screenshots and log dumps, LogRocket lets you replay the session to quickly understand what went wrong. It works perfectly with any app, regardless of framework, and has plugins to log additional context from Redux, Vuex, and @ngrx/store.
 
In addition to logging Redux actions and state, LogRocket records console logs, JavaScript errors, stacktraces, network requests/responses with headers + bodies, browser metadata, and custom logs. It also instruments the DOM to record the HTML and CSS on the page, recreating pixel-perfect videos of even the most complex single-page apps.
 
[Try it for free](https://logrocket.com/signup/).
* * *

The post [Serialization in NestJS: A different approach](https://blog.logrocket.com/serialization-in-nestjs-a-different-approach/) appeared first on [LogRocket Blog](https://blog.logrocket.com).