Subscribers a.k.a Entity Listeners of TypeORM on NestJS
How and why we use them?
Imagine that you are building an archetypal e-commerce app backend. Generic product with properties etc. After a while, you comprehend that you need to inform users about changes in your inventory like stock changes, price changes, new arrivals.
What is Entity Listener?
It is a database specialized listener that listens to the CRUD events that happening in your database.
In TypeORM, It subscribes to your entity and listens to the changes.
After this point, I will use a Subscriber because of TypeORM wish to use it
What type of Subscribers TypeORM has?
I am not going into detail about each of them. It looks quite clear, right?
An Example
We will be developing a small application with only product service.
I will presume that you already installed and spinned up a NestJS app
or you may choose the shortcut for it :
Let’s say we have a very essential product with the following properties:
- Name
- Quantity
- Price
Let us define our entity like the below snippet.
What about some magical decorators :
@CreateDateColumn, @UpdateDateColumn and @DeleteDateColumn
to handle dates for us ?
They act like subscribers. When the decorated action happens, they will update the date
A service to access the database via repositories. Be careful about how we handled the Update operation
Why did I used save for Update operation ?
We can’t access the changed data via update command over repositories with EventSubscriber.
I don’t prefer to use listeners in Entities so here is the subscriber:
You can implement beforeInsert function to hash the user passwords before saving into the database
We implemented the EntitySubscriber interface for the Product partially. We didn’t implement other listeners but you can see them with the help of IntelliSense or CodeCompletion tool you like.
listenTo function Indicates that this subscriber-only listens to Product entity related events. You may choose to listen to all entities but I’m not going to explain it here for the context.
afterUpdate function works when the product price changes afterUpdate function runs and logs the changed price values into the console via a built-in logger.
Can it be declared dynamically?
There’s no documented way to do it but with the power of the Dependency Injection, we can achieve to get it injected by NestJS:
Then we no longer need a subscriber property in TypeORM configuration.
Tip: Publish a message with Redis or RabbitMQ to notify price changes for sending notifications to users that track this product
What about unsubscription?
I didn’t find anything about this one yet but I’ve seen metadata properties in the repository to get active listeners that’s all I know.
Here is the controller:
Normally, I would use a class-validator with DTO’s but I wanted to keep it short in this controller.
Our product.module to glue the parts that we developed so far
Finally the app.module. Don’t forget to add entities and subscribers like in the below snippet
Do not use such configuration for TypeORM like above 👆 Prefer Config Service approach to make it configurable
If everything goes right and you start your app with yarn start: dev
Let’s add a product first :
Then update the product price
And we get the change!
Conclusion
Having such event listeners helps to catch changes on data easily and in a more clean way instead of polluting your entity class with such business logic.
If you have any questions feel free to ask!
Thanks for the reading and you can find me on Twitter and GitHub!