Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Ibuki - PUB-SUB Messaging and Data Service for Node.js

0.00/5 (No votes)
23 Jan 2023 1  
Send and receive messages in a node.js application in any place of the code. A message can be any object and it can be of arbitrary size.
Ibuki is a PUB-SUB messaging and data service for node.js which greatly simplifies intra module communications.

Introduction

When you create a node.js server side application, soon it grows quite big consisting of several modules. In such an application, it becomes challenging to send data from a module X to a module Y. A requirement is felt to have a centralized messaging cum data service which enables you to send and receive arbitrary chunk of data irrespective of the module or place of code. You can certainly do it using eventEmitter mechanism, but here I am going to show a more versatile, generic and unified way of doing so. Ibuki data service attempts to resolve this problem in a systematic manner. This utility is a must have code snippet for any node.js application.

Background

Way back in year 2016, I was working with a big banking project in backbone.js. There, it was a nightmare to send object messages from one place to another. They had come up with a concept of backbone.radio channel which facilitated sending messages in publisher / subscriber fashion in a great manner. But this had got limitations. Later, in 2017, when I was working with Angular 2, I came across the great rxjs library. This library is a revolution in programming. This unleashes a new way of programming called reactive programming. I realized that the concept of backbone.radio channel can be combined with rxjs to create a very robust and well organized pub / sub service which is fail proof in heavy traffic conditions. I ended up creating this Ibuki service which I used in over 100 projects successfully. Ibuki is a Japanese word which means breath. In a way, Ibuki service is a breath control service for a massive node.js application.

What Business Problem Ibuki Solves

Imagine the situation when your node.js application has grown up to 100+ modules or files. You need to send data from one module to another module and the entire communication needs to be asyncronous. Ibuki data services can be used to send named messages from any module to any other module. I have tested it in heavy traffic conditions and it has never let me down. Ibuki provides a very unique, simplified and generic way to do such communications. Through proper use of Ibuki, you can convert deeply nested asyncronous calls to single level of nesting which can highly simplify the code and make it understandable to other team members.

I have also successfully used Ibuki in several Angular 2 / 4 / 5 / 6 applications using it as an Angular service. I will discuss Ibuki usage in Angular in a separate blog article. For Angular, there are a few small changes to be made in Ibuki code like making use of import statements instead of require statement.

I say Ibuki is not merely a small piece of code but it is a different concept of thinking in an asyncronous world of programming. If you use node.js at server side and JavaScript at client side in single page application, then Ibuki enables to use the same control mecanism and similar code in both client and server side. This highly simplifies the work and increases the throughput. In fact, through proper adjustments, Ibuki concept can be used in any JavaScript based SPA or node.js application.

Why You Need Multiple Modules in node.js Application

Multiple modules make it easy to manage your node.js application when it grows big. Say for example, your node.js application connects to database, it does some file input / output operations. It makes some http calls to REST enabled web services, it does some housekeeping work, it listens to inbound HTTP calls and it also contains entire business logic. If you create a single module or a file to do all the above work, then you will start scratching your head some day.

In such situations, it is suggested that you create modules such as database.js, fileio.js, http-calls.js, houskeeping.js, routing.js, business-logic1.js, business-logic2.js ... and so on. Modulerizing your application like this will simplify its understanding to other developers also. Say for example, all database related code is in database.js module. In node.js paradigm, most of these modules need to communicate with each other in an asyncronous manner transferring an arbitrary size and structure of data from one module to another. This is where Ibuki service comes to the rescue.

An Entire node.js Application using Ibuki

Now I will walk through an entire node.js app which makes use of Ibuki service for intra modules communication. I am keeping the app very simple only to explain the basic idea of object messaging in node.js through Ibuki service. The app has only three modules or files. They are index.js, ibuki.js and http-calls.js.

The index.js is an entry point, ibuki.js is the Ibuki service module and http-calls.js is a module which does all http-calls to external APIs. Flow of the app is as follows:

  • User browses the url http://localhost:8080/states
  • The index.js module in the app is hit because this is the entry point and this implements the http endpoint for the said url.
  • The index.js makes an asyncronous call to http-calls module by using ibuki module and passes it the response object and an external REST API url wrapped in an object.
  • The http-calls module calls the external REST API and updates the response object and sends back the response to user's browser.
  • The user sees the states in India on the browser screen in JSON format.

index.js

JavaScript
'use strict';
const path = require('path');
const express = require('express');
const ibuki = require('./ibuki');
const httpCalls = require('./http-calls');
const app = express();

// CORS
app.use((req, res, next) => {
    res.header("Access-Control-Allow-Origin", "*");
    res.header('Access-Control-Allow-Methods', ' GET,PUT,POST,DELETE');
    res.header("Access-Control-Allow-Headers", 
               "Origin, X-Requested-With, Content-Type, Accept, Authorization");
    next();
});

app.get('/states', function (req, res) {
    const obj = {
        url: ' http://services.groupkt.com/state/get/IND/all',
        res: res
    };
    ibuki.emit('get:states', obj);
});

app.listen(8080, _ => console.log('Running on port ' + 8080));

ibuki.js

JavaScript
'use strict';
var ibuki = {};
const rx = require('rxjs');
const operators = require('rxjs/operators');
const subject = new rx.Subject();
ibuki.emit = (id, options) => {
    subject
        .next({
            id: id,
            data: options
        });
}
ibuki.filterOn = (id) => {
    return (subject.pipe(operators.filter(d => (d.id === id))));
}
module.exports = ibuki;

http-calls.js

JavaScript
'use strict';
const axios = require('axios');
const ibuki = require('./ibuki');
let httpCalls = {};
ibuki.filterOn('get:states').subscribe(
    d => {
        const obj = d.data;
        axios.get(obj.url)
            .then(result => {
                const states = result.data.RestResponse.result;
                obj.res.json(states);
            })
            .catch(err => {
                obj.res.send(err.message);
            })
    }
);
module.exports = httpCalls;

Explanation of Code

index.js

The following code in index.js is relevant code which receives the http request.

JavaScript
app.get('/states', function (req, res) {
    const obj = {
        url: ' http://services.groupkt.com/state/get/IND/all',
        res: res
    };
    ibuki.emit('get:states', obj);
});

In the above code, an object obj is created and this is published through ibuki.emit('get:states', obj). The obj is arbitrary data which wraps the external REST API url and the response object. Now all the subscribers who have already subscribed to 'get:states' will get the data in form of obj. The http-calls module is the one which already subscribes to 'get:states' hence it will receive the obj.

http-calls.js

Following is the relevant code in http-calls.js which subscribes to 'get:states'.

JavaScript
ibuki.filterOn('get:states').subscribe(
    d => {
        const obj = d.data;
        axios.get(obj.url)
            .then(result => {
                const states = result.data.RestResponse.result;
                obj.res.json(states);
            })
            .catch(err => {
                obj.res.send(err.message);
            })
    }
);

In the above code, the ibuki.filterOn('get:states').subscribe(... is responsible for subscription. So when a call is received at index.js, the data is transferred to http-calls.js through ibuki module in the form of obj. The obj has the information of url to be called. By using the axios library, a get request is made to the url which returns a promise. In the success of promise (.then) the states object is created and this data is pushed to the http response through the line obj.res.json(states); In case of error, the error message is sent through the response object through the code obj.res.send(err.message);

How to Install and Run this Demo Application from my GitHub

Download the code from GitHub:

git clone https://github.com/capitalch/ibuki-node-demo

Change directory in Windows environment:

cd ibuki-node-demo

Install dependencies:

npm install

Node.js should be preinstalled in your system. Run the node.js server:

node index

Now in your browser, navigate to:

http://localhost://8081/states

You will see the following screen:

Source Code Included

I have included all code files in the form of ibuki-node-demo.zip. You can download it. You need to run npm install to install all dependencies in package.json file before running the application.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here