Road to microservices with node js events and RabbitMQ

Rémi Goyard
5 min readJul 13, 2019

--

In this post, I will show you how to prepare your nodeJS application to microservices using events, and then split your application easily

Imagine the following scenario

  • A web frontend (made using your favorite framework)
  • A backend made in node js exposing some routes (REST, GraphQL, or anything you need)

The backend application is huge, and you need to add some features at some key points of the code, for example sending an email to the user after registration, and “maybe”, one day, create the user as a customer in your CRM application.

One way to achieve this can be to add one function call for each need at the end of the registerUser method, something like that :

This was has worked for decades, and still works, so if it fits your needs, it’s fine.

But as I don’t really know what, in the future, I will have to do after a user has registered, I will, now, prefer another way using events.

Using events, you will “emit” a USER_HAS_REGISTERED event at the end of the registerUser function with as payload the user. And somewhere else in your code you can create "listeners" to do something when the event is fired.

It will be something like that :

The idea is to “decorrelate” the registerUser function and some of the actions you want to do when a user registers.

Another benefit is that if the function registerUser is 100% test covered (and it is I'm sure ...), adding some feature after a user registers will not impact the code of the registerUser function.

A big warning before we continue … Events in Node.JS are a little bit counter-intuitive, I highly recommend you to read my article on this Understand Events with Node.JS

So, let’s see a small example to work with events.

A small “monolithic” example

We will create a small “monolithic” application using ExpressJS, actually, the application is only exposing one route, POST /event for firing an event.

For listening, and emitting events, I use nodejs-event-manager (based on Emittery).

The package nodejs-event-manager will help you to migrate to RabbitMQ later (as they both expose the “same” API).

The code source of the small “monolithic” application is here : https://github.com/mimiz/road-to-microservice

First we will create an EventManager.ts file to "encapsulate" the nodejs-event-manager :

See the documentation of nodejs-event-manager, to check about it’s options.

Then, the only route exposed :

And the listeners :

It’s trivial, and of course, in your real application, you will externalize handlers in modules.

Then the main index.ts file

So now, if you post (with postman) an event on the main route :

You may see the following output in your server console :

Our listener caught the events and do the stuff they had to do … You can try with the other event …

NOTE: As you can see the nodejs-event-manager add a _metas_ key to the payload, this can be useful for logging, but it can be disabled (or override), please read the documentation if you want to know how to do it.

This can work, because nodeJS is “mono-threaded”, so all listeners and emitters are sharing the same EventManager instance.

So as we want to split our small “monolithic” application into “micro” applications, we need to user “something” for sharing/storing/distributing events … Let’s do it with RabbitMQ.

Prepare the migration.

First, we will set up our small “monolithic” application to use RabbitMQ, as an “EventManager”, so the application will still be “ monolithic “ but all events will be distributed and listened via RabbitMQ.

If you don’t know anything about RabbitMQ, I strongly recommend to read (and do) the RabbitMQ Tutorials.

You can either, start a local RabbitMQ server, or use CloudAMQP — RabbitMQ as a Servioce. For the purpose of this article, I will use a local (docker) RabbitMQ Server.

(See the Docker RabbitMQ Official Page for more options about the Docker RabbitMQ Official Image)

So let’s replace the nodejs-event-manager by the rabbitmq-event-manager dependency. And change the eventManager.ts file :

As you can see, I just changed the dependency, and add the URL when creating the instance of the EventManager.

If you start your application and look at RabbitMQ admin application, you will see that two Exchanges have been created with the name of the events you are listening.

Two queues have also been created with the name of the application and the name of the event.

So if you post on the /event route, the following :

Your server console will print out

So it worked as expected.

Now if you comment the line eventManager.on('USER_HAS_REGISTERED', userRegisteredHandler); in listeners.ts, restart your server, and post a USER_HAS_REGISTERED event, you will see that the server doesn't log anything (it's normal, we do not listen to the event), but the event is stored in the monolith::USER_HAS_REGISTERED queue, waiting for listeners.

Then uncomment the line, and restart your server, it should print out the event quite immediately, and so empty the queue. Great no?

Now create our “micro” applications

So now we will split the monolith into three applications :

  • One for the Express APP (API)
  • One for the userRegisteredHandler
  • One for the anotherEventHandler

Create the API Application

This is our small “monolithic” application, without listeners, here is the index.ts file modified :

Nothing else… Just remember to remove the unused file listeners.ts. That's all.

Create the userRegistered Application

This “micro” application is a simple listener, so we can create it with only one index.ts file :

Create the anotherEvent Application

Just copy / paste and adapt from the userRegistered application :

Now you may be able to start 3 nodeJS processes and try to post an event on the API, and you will see the event logged in the console of the micro-application listening for this event.

So now we migrate from a “monolithic” application to microservices.

Now you can, if you want, dockerize everything and deploy on your favorite cloud provider.

The source code of all application is shared on my Github road-to-microservice

Thanks for reading

--

--