Chapter 2:

Node.js MQTT

March 30, 2022
10 min

Node.js MQTT

The Internet of Things (IoT) model consists of several interconnected components. At a high level, they are:

  1. The “Thing” (a physical or virtual device)
  2. The cloud backend
  3. The User Interface (like an app, API, or a website)
Representation of IoT (Source) (CC license)

All of these components need to be able to exchange information to make IoT applications work. This is where communication protocols come in. 

One of the most popular IoT communication protocols is MQ Telemetry Transport, orMQTT. Using the popular Node.js server-side scripting language with MQTT, we’ll provide a step-by-step tutorial that shows you how to:

  1. Create an application in Node.js that can publish and subscribe to MQTT topics
  2. Understand the relevant Node.js library to configure other MQTT settings like Last Will and Testament (LWT), retained messages, different QoS levels, etc.

MQTT Overview

What is MQTT and why is it so important in IoT? Let’s answer both these questions one by one.

What is MQTT?

MQTT is a publish-subscribe communication protocol that organizes data into topics, orchestrated by a broker. Think of the broker as a compartmentalized bulletin board (the compartments representing topics).

MQTT in action

Each MQTT message has an associated Quality of Service (QoS), determining the guarantee of delivery of the message. It can either be at most once (0), at least once (1), or exactly once (2).

MQTT also boasts of several other features that find a lot of applications in IoT. It supports ‘retained’ messages, wherein the broker can retain a copy of a message on a topic and send it to any future subscriber to that topic (helps send critical communication to currently offline devices, whenever they come online).

There is the Last Will and Testament (LWT), which is essentially a message a client tells a broker to publish on a topic if it gets disconnected unexpectedly (this can often help identify devices that suddenly lose internet connectivity). 

Why is MQTT important in IoT?

Apart from the features highlighted above, MQTT also benefits from is lightweight and bidirectional. IoT devices are often resource-constrained (in terms of power and bandwidth). Therefore, the less resource-intensive the protocol, the better. 

Similarly, the bi-directional client-broker model of MQTT allows for one-to-one, one-to-many, many-to-one, and many-to-many communication, greatly increasing the flexibility and suiting a wide range of IoT applications.

Node.js Concepts

Before you jump into the tutorial, you should understand these two Node.js concepts:

  • Promise: As the name suggests, it is an assurance that something will be returned by the asynchronous process. It can be ‘resolved’ when the process completes or ‘rejected’ if the process faces an error in execution, and remains in ‘pending’ state while the process is in operation.
  • Async-await: A wrapper around promises that help async code easier to read. The await keyword used before any promise halts the code execution at that line till the promise is fulfilled. The async keyword is used in front of a function declaration to make it an asynchronous function. 📝Note: The await keyword can be used only within an async function.
Global Edge Computing Platform
Platform
Azure IoT Edge
AWS IoT Greengrass
Macro
meta
Real-Time Event Processing
✔️
✔️
✔️
Internet Scale Throughput
✔️
✔️
✔️
Stateful Edge Device Processing
✔️
✔️
✔️
Cross-Region Replication
✔️
✔️
Geo-Fencing and Data-Pinning
✔️
Start a free trial
Platform
Real-Time Event Processing
Internet Scale Throughput
Stateful Edge Device Processing
Cross-Region Replication
Geo-Fencing and Data-Pinning
Azure IoT Edge
✔️
✔️
AWS IoT Greengrass
✔️
✔️
✔️
Macrometa
✔️
✔️
✔️
✔️
✔️

How to Use MQTT with Node.js

Now that you understand the basics of MQTT and Node.js, let’s move on to the hands-on tutorial.

Prerequisites

  • Basic experience in Node.js programming
  • Familiarity with MQTT will help (which we covered above!)

Download Node.js if you don’t already have it. If you are not sure whether Node.js is installed on your machine, open up a terminal window and enter the following command:


node --version 

If it provides you with a version number, you are good to go.

Install the MQTT.js package

We’ll be using the MQTT.js package in this tutorial. You can find the source code here. Run this npm command to install it:


npm install mqtt

Once installed, you can import the package in your code as follows:


const mqtt = require('mqtt')

Connecting to a broker

If you see the API reference, there are two ways to connect to a client: pass in a client URL or add an options JSON. Let’s see examples with both.

Using URL:


const client  = mqtt.connect('mqtt://test.mosquitto.org');

Using options:


const options = {
    port: 1883,
    host: 'broker.hivemq.com',
    clientId: 'myclient',
};
const client = mqtt.connect(options);

Here, we define the clientID (which is an identifier for the broker), and we connect to the host (broker) over the specified port. Note that the client ID needs to be unique, i.e., two clients sharing the same client ID cannot communicate with the broker at the same time. 

These aren’t the only settings you can specify in the options JSON. You can find the exhaustive list in the MQTT readme. You can add all the relevant settings (keep-alive time, last-will, etc.) that you require in the JSON. We will see a more detailed example in the tutorial below.

Once the connection is successful, a ‘connect’ event will be triggered. You can take relevant actions, like subscribing to topics, or publishing to topics whenever the event is triggered.


client.on('connect', function () {
  //Do whatever you want to do on connection
});

Publishing and Subscribing

For publishing, the name of the topic and the message are required fields. Optional settings, like QoS level, whether the message should be retained, etc., can be specified in the options json. You can also optionally specify a callback function to be executed when the QoS handling is complete, or, in the case of QoS 0, at the next tick.


let pub_topic = 'ys/pub';
let message = 'Greetings from client';
let pub_options = {qos:0, retain:true};
client.publish(pub_topic, message, pub_options, function(err) {
            if(err) {
                console.log("An error occurred during publish")
            } else {
                console.log("Published successfully")
            }
        });

You can read more about the publishing options in this section of the MQTT readme. The subscribe syntax is similar. The only differences are that there is no message argument, and instead of a single topic, you can specify an array of topics to subscribe, or even a object, with different QoS levels for each topic, like {‘topic1’:{‘qos’:0},’topic2’:{‘qos’:2}}. An example snippet is given below:


let sub_topic = 'ys/sub';
let sub_options = {qos:0};
client.subscribe(sub_topic, sub_options, function (err) {
        if (err) {
            console.log("An error occurred while subscribing");
        } else {
            console.log("Subscribed successfully");
        }
    });

Apart from publish and subscribe, there are other methods and properties of the Client class that you should be aware of. Here is a brief overview of the important methods and properties:

Client class methods and properties
Name Type Description
.unsubscribe() Method Unsubscribe from a topic
.end() Method Close the client connection
.reconnect() Method Connect again using the same options as connect()
connected Property true if the client is connected to the broker
reconnecting Property true if the client is trying to reconnect

Events

In the ‘Connecting to a broker’ section above, we saw the example of one event: ‘connect’. There are several other events as well (you can find the API reference in this section of the MQTT readme). A brief description of the frequently used events is below. You can trigger relevant actions for each event.

MQTT Events
Event name Arguments available for the triggered function Triggered on
connect connack Successful connection/ reconnection
message topic, message, packet Reception of a published message
reconnect Start of reconnection
error error Error in connecting or parsing
offline Client going offline

Node.js and MQTT Tutorial Project

Now that you know the basics of implementing MQTT in Node.js, let’s create a simple Node.js MQTT application that publishes to a topic every 5 seconds. Additionally, this application subscribes to another topic and logs all the incoming messages on that topic. 

Introduction to the broker

We will be using the public broker provided by HiveMQ. The primary reason for using this over other public brokers is the availability of a web dashboard for viewing incoming messages, and for sending messages to the connected clients. You can find the dashboard on the official HiveMQ website.

An additional benefit is that you don’t need to sign-up or obtain any credentials. You can just enter the correct host and port in your application and get started.

Web dashboard provided by HiveMQ, for interacting with connected Clients

Preparing the code

The application code below is heavily commented to help you better understand what it means.


const mqtt = require('mqtt');

// Connection variables
let broker_host = 'broker.hivemq.com';
let broker_port = 1883;
let client_id = 'ys_client';

// Publish variables
let pub_topic = 'ys/pub';
let message = 'Greetings from ' + client_id.toString();
let pub_options = {qos: 0, retain: false};

// Subscribe variables
let sub_topic = 'ys/pub';
let sub_options = {qos: 0};


const connection_options = {
    port: broker_port,
    host: broker_host,
    clientId: client_id,
    reconnectPeriod: 5000 // Try reconnecting in 5 seconds if connection is lost
};

const client = mqtt.connect(connection_options);

client.on('message', function (topic, message) {
    console.log("Received message " + message.toString() + " on topic: " + topic.toString());
})

client.on('connect', async function () {
    console.log('Connection successful');
    client.subscribe(sub_topic, sub_options, function (err) {
        if (err) {
            console.log("An error occurred while subscribing")
        } else {
            console.log("Subscribed successfully to " + sub_topic.toString())
        }
    });

    while (client.connected) {
        client.publish(pub_topic, message, pub_options, function (err) {
            if (err) {
                console.log("An error occurred during publish")
            } else {
                console.log("Published successfully to " + pub_topic.toString())
            }
        });

        // Delay of 5 seconds
        await new Promise(resolve => setTimeout(resolve, 5000));
    }
})

// Handle errors
client.on("error", function (error) {
    console.log("Error occurred: " + error);
});

// Notify reconnection
client.on("reconnect", function () {
    console.log("Reconnection starting");
});

// Notify offline status
client.on("offline", function () {
    console.log("Currently offline. Please check internet!");
});

If the connection is successful, we publish to the broker on the specified topic every 5 seconds. If the connection is lost, we attempt re-connection every 5 seconds (specified by the reconnectPeriod setting in connection_options JSON). We log all events on the console, including error, reconnection, and offline.

Testing it locally

Open a terminal window and navigate to the directory containing the file you just saved. Enter the following command:


node <file_name> .js 

You should be able to see all the events logs in the terminal window. Try turning off the internet on your machine briefly, and then turning it back ON again. You should be able to see the system auto-recover.

Log of events on the terminal window

You should also be able to view the logs on the HiveMQ dashboard. You can also publish from the dashboard and see the message being received on your terminal window.

Checking logs on HiveMQ dashboard

Node.js and MQTT Best Practices

Moving forward, keep these best practices in mind when you work with Node.js and MQTT.

  • Avoid triggering unnecessary events. For example, setting the ‘reconnectPeriod’ setting in the options is much cleaner than manually calling client.reconnect()
  • Keep track of your promises. A code that quickly exits often hints at an unfulfilled promise that you forgot to wait for. Use async-await wherever you need to wait for the promise fulfillment before proceeding.
  • Use QoS 1 and 2 only if required. Depending on your broker, they can often lead to extra charges for the ACK messages
  • Read the documentation. Often, the setting you are looking for exists, and all you need to do is skim through the documentation.
Global Edge Computing Platform
Start a free trial
Stateful geo-replicated stream processing keeps globally distributed data consistent
One integrated platform for streams, key values, docs, graphs, and search simplifies development
Declarative configuration using JavaScript and SQL avoids the need to learn a new syntax
Start a free trial

Summary

In this article, you learned how to implement MQTT in Node.js using the MQTT.js package. We covered establishing a connection, publishing, subscribing, and event-handling. Then, you used these concepts to create a sample project in the Node.js and MQTT tutorial. Now you’re ready to  build your own custom IoT applications! 

Further reading