Unlocking the Power of Serialization in Your Applications
When dealing with objects in our applications, we often encounter sensitive information that needs to be protected. For instance, a user object fetched from a database may contain a password that should not be exposed to the end client. Additionally, some information saved in an object may not be useful for the client and should be removed to save bandwidth. This is where serialization comes in – a process of preparing an object to be sent over the network to the end client.
The Limitations of Out-of-the-Box Serialization
NestJS provides a way to serialize objects returned from API endpoints using a decorator and a library called class-transformer. While this solution works for basic cases, it falls short in more complicated scenarios. For example, if we want to create a findAll method that returns multiple user objects, we cannot simply return an array of objects because the serialization mechanism will not work as expected.
Creating a Reusable Serialization Solution
To overcome these limitations, we need to create our own serialization mechanism that provides more flexibility and control. This involves implementing two key components: a “parent” class that every serializer will extend, and an interceptor that will take care of running our serializers.
The Flow of Serialization
Our serialization mechanism will work as follows: each controller marks which properties should be serialized, and then the interceptor goes over the keys of the returned object and serializes the values that were marked. We can “mark” an object by wrapping it into a class called Serializable, which keeps a reference to a function that will be used to serialize a value.
BaseSerializerService
To create our base serializer, we will create an abstract class called BaseSerializerService that provides reusable methods for all serializers. This class takes two generic types, E and T, which stand for an entity and a serialized value, respectively. Each serializer will implement its own serialize method, which takes an entity and a user role, and returns a serialized object.
Serialization Methods
Our serialization mechanism will provide several features, including asynchronous serialization, nested serialization, and adding additional properties that weren’t in the original object. We can also implement methods to serialize collections of entities and wrap values in a Serializable class.
SerializerInterceptor
To create our interceptor, we will use Nest’s interceptor mechanism, which allows us to transform the object returned from a controller method. Our interceptor will check whether the response is an object, and if so, it will use the serializeResponse method to serialize the object.
Real-World Use Case
Let’s consider a real-world use case where we have a user entity that represents an author, and it references many articles that the author wrote. Our goal is to make sure that the password property is removed and the nested articles are also serialized. We can achieve this by using the articleSerializerService to serialize an article instead of writing the same logic in the userSerializerService.
Summary
By implementing our own serialization mechanism, we have gained a lot of flexibility and control over how the process is executed. This implementation is reusable and can be quickly adapted to any existing or freshly created project. With this approach, we can ensure that our applications are secure and efficient, and provide a better experience for our users.