How to use ZeroMQ Request-Reply Pattern in Node.js

franciscomendes10866

Francisco Mendes

Posted on September 11, 2021

How to use ZeroMQ Request-Reply Pattern in Node.js

Overview

I bet many of us have thought about decopulating a backend and splitting it into microservices. Let's say you have a monolithic backend and then you decide to add something like a newsletter and you'd rather have a microservice that has the sole function of sending emails.

On the internet you will find several solutions to solve this challenge, but one of the most common is the use of a message broker. However, not all of us need a solution as advanced as the use of a message broker, it is in these specific cases (smaller applications) that I like to use ZeroMQ.

If you don't know ZeroMQ, that's okay because it's a technology that isn't widely shared in the community, so if you want to know more about ZeroMQ, I recommend reading this article, which will give you a better introduction than me.

Today's example

The idea of today's example is to create a simple Api (server) that will receive a json property that will be the text and then we will send that same text to an app (worker) that will be responsible for calculating the length of this same string. Finally we will return the string length to the Api and then it is sent in the response body.

The framework I'm going to use is Fastify and the ZeroMQ client that I'm going to use is zeromq.

Let's code

As you may have already understood, we are going to have two backends. One of the backends we will call a server, which will be our Api. The other backend will be the worker, which will be our small microservice.

First and foremost, let's install our dependencies:

npm install fastify zeromq --save
Enter fullscreen mode Exit fullscreen mode

Now let's create a simple API:

// @/server.js
const Fastify = require("fastify");

const app = Fastify();

app.post("/", (request, reply) => {
  const { text } = request.body;
  return reply.send({ text });
});

const main = async () => {
  try {
    await app.listen(3000);
  } catch (err) {
    console.error(err);
    process.exit(1);
  }
};
main();
Enter fullscreen mode Exit fullscreen mode

As you can see, let's get the text from the request body object and then send it in the response body.

Now we can import zeromq and create an instance of it. Then we will create our ZeroMQ socket of the Request type and we will accept connections through an address defined by us, however this is asynchronous and has to be done as soon as the application is started. Like this:

// @/server.js
const Fastify = require("fastify");
const zmq = require("zeromq");

const app = Fastify();
const sock = new zmq.Request();

// ...

const main = async () => {
  try {
    await sock.bind("tcp://*:7777");
    await app.listen(3000);
  } catch (err) {
    console.error(err);
    process.exit(1);
  }
};
main();
Enter fullscreen mode Exit fullscreen mode

Afterwards at our endpoint we will send the text we got in the body of the request object, however before we send it we have to convert the json to string.

app.post("/", async (request, reply) => {
  const { text } = request.body;
  await sock.send(JSON.stringify({ text }));
  return reply.send({ text });
});
Enter fullscreen mode Exit fullscreen mode

Still on our endpoint, after we send the text we'll wait for the response from our worker.

app.post("/", async (request, reply) => {
  const { text } = request.body;
  await sock.send(JSON.stringify({ text }));
  const [result] = await sock.receive();
  return reply.send({ text });
});
Enter fullscreen mode Exit fullscreen mode

Now we have to be careful because what we're going to get from our worker is a buffer so we have to handle it to get the text length value.

After that we have to convert the result from string to number, only after that we can send the result in the response body.

app.post("/", async (request, reply) => {
  const { text } = request.body;
  await sock.send(JSON.stringify({ text }));
  const [result] = await sock.receive();
  return reply.send({ length: Number(Buffer.from(result)) });
});
Enter fullscreen mode Exit fullscreen mode

Our server code should look like this:

// @/server.js
const Fastify = require("fastify");
const zmq = require("zeromq");

const app = Fastify();
const sock = new zmq.Request();

app.post("/", async (request, reply) => {
  const { text } = request.body;
  await sock.send(JSON.stringify({ text }));
  const [result] = await sock.receive();
  return reply.send({ length: Number(Buffer.from(result)) });
});

const main = async () => {
  try {
    await sock.bind("tcp://*:7777");
    await app.listen(3000);
  } catch (err) {
    console.error(err);
    process.exit(1);
  }
};
main();
Enter fullscreen mode Exit fullscreen mode

Now we can start working on our worker. Now let's import zeromq and create an instance of it. Then we will create our ZeroMQ socket of the Reply type and we will accept connections through the address that we defined before.

// @/worker.js
const zmq = require("zeromq");

const sock = new zmq.Reply();

const main = async () => {
  try {
    sock.connect("tcp://localhost:7777");
    // ...
  } catch (err) {
    console.error(err);
    process.exit(1);
  }
};
main();
Enter fullscreen mode Exit fullscreen mode

Then let's create a for loop so that it calculates each of the messages we receive via our server. Once we get the message we have to convert it from string back to json so we can read the text value and then we will log it.

After this we will calculate the length of it, but we have to take into account that before sending the length of the text we have to convert the value to string.

// @/worker.js
const zmq = require("zeromq");

const sock = new zmq.Reply();

const main = async () => {
  try {
    sock.connect("tcp://localhost:7777");
    for await (const [msg] of sock) {
      const text = JSON.parse(msg).text;
      console.log(`Received: ${text}`);
      await sock.send(String(text.length));
    }
  } catch (err) {
    console.error(err);
    process.exit(1);
  }
};
main();
Enter fullscreen mode Exit fullscreen mode

Now when testing our Api with a tool similar to Postman, you can send a json object in the request body with the text property.

testing api

Then you should have something similar to this on your terminal:

terminal logs

Conclusion

As always, I hope you found it interesting. If you noticed any errors in this article, please mention them in the comments. 🧑🏻‍💻

Hope you have a great day! 👋

💖 💪 🙅 🚩
franciscomendes10866
Francisco Mendes

Posted on September 11, 2021

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related