The Ultimate Guide on How to Use MQTT with Node.js

The Ultimate Guide on How to Use MQTT with Node.js

author Peter Giacomo Lombardo

Written by Peter Giacomo Lombardo

Category: HiveMQ MQTT IoT Node.js

Published: May 16, 2023


Node.js is a popular server-side platform for building scalable and robust web applications. With the continued popularity and growth of MQTT in many use cases, it’s already well-established in the Node.js ecosystem.

MQTT (Message Queue Telemetry Transport) is a lightweight protocol for exchanging data between devices over a network. It uses the publish <–> consumer model, particularly the HiveMQ MQTT broker, which can scale up to 200 million connections!

In this article, we will explore how to use MQTT with Node.js, covering topics, such as subscribing to a topic, publishing an MQTT message, QoS levels, error handling, etc. The article also covers advanced use cases and touches on related topics such as payload management.

Before diving into the details, let’s first understand MQTT and how it works.

For Neophytes – Introduction to MQTT and Node.js

Node.js

If you’re not familiar with Node.js, it’s an event-driven runtime environment built using Javascript. It allows developers to use javascript to build server-side applications and makes it easier to create responsive and performant applications that can handle many simultaneous requests.

Thanks to its lightweight and event-driven architecture, Node.js can also run on resource-constrained devices making it a popular choice for building IoT applications. Additionally, Node.js has several libraries and tools specifically designed for building IoT applications, such as the Johnny Five framework and Node-RED - a visual programming tool.

Because of these strengths, Node.js is a popular choice for companies such as Netflix, Bosch, Siemens, Honeywell, NASA and many more.

MQTT

MQTT is a messaging protocol that allows devices to communicate with each other over a network. It was originally developed in 1999 by Andy Stanford-Clark of IBM and Arlen Nipper of Arcom Control Systems. However, the OASIS MQTT Technical Committee currently manages it, and is responsible for maintaining and evolving the MQTT specification.

MQTT is designed to be lightweight and efficient, making it an ideal choice for IoT (Internet of Things) devices. It uses a publish/subscribe model, where publishers send messages to a broker, which then distributes them to subscribers who have subscribed to the relevant topics.

With that done, let’s get to it.

MQTT Package Options in Node.js

A few popular MQTT client packages for Node.js provide a range of features and functionalities. Here are some of the most popular MQTT client packages:

  1. MQTT.js: MQTT.js is a client library for the MQTT protocol that supportsNode.js and web browsers. It provides a simple API for connecting to an MQTT broker, subscribing to topics, and publishing messages. It is widely used and has a large and active community.
  2. mqtt-async: mqtt-async is an async wrapper around the MQTT.js package. It allows you to use Promises that were introduced in ECMAScript 6 (ES6).
  3. aedes-client: aedes-client is an MQTT client library for Node.js bundled with the Barebone Aedes MQTT broker. It supports most MQTT features. We won’t be covering this client in this article.

For the rest of this article, we will mainly focus on the MQTT.js package, but will also cover mqtt-async towards the end.

CommonJS Versus ES6

Before diving in, it’s good to make sure we know these two package paradigms in Javascript. If you’re a seasoned Node.js and Javascript developer, you likely already know this and can skip to the next section. The important part to note is that MQTT.js supports both paradigms.

CommonJS and ES6 are two different module systems used in JavaScript. CommonJS is the module system used in Node.js, while ES6 is the module system used in modern web browsers and newer versions of Node.js.

CommonJS modules are loaded synchronously and use the require function to import modules. For example:

1
const mqtt = require('mqtt');

ES6 modules are loaded asynchronously and use the import statement to import modules. For example:

1
import mqtt from 'mqtt';

The MQTT.js package supports both CommonJS and ES6 module systems. You can use either require or import to import the package. By supporting both module systems, the MQTT.js package opens itself up to various JavaScript environments, including both Node.js and modern web browsers.

The MQTT.js package supports both, but it’s good to know the differences and how they may affect your code. For the rest of this article, we will be using the ES6 syntax.

Installing MQTT.js

This section assumes you already have a functioning Node.js installation locally. If you don’t, check out https://nodejs.org/en to install Node.js.

Before we start working with MQTT.js, we need to install the MQTT package. We can do this using npm (Node Package Manager). Open your terminal and type the following command:

1
npm install mqtt

or alternatively via Yarn:

1
yarn add mqtt

Browser CDN Support

The MQTT package is also available through unpkg here.

Further, the package has instructions on how to use the package with Browserify, React, and Webpack. See the Github page for these instructions.

Connecting to an MQTT broker

The MQTT protocol uses the concepts of clients (what we cover here in this article) and Brokers. An MQTT broker is a server that acts as an intermediary between MQTT clients.

For a more in-depth explanation of MQTT brokers and clients, see Introducing the MQTT Protocol - MQTT Essentials: Part 1.

For the rest of this article, we’ll use HiveMQ’s public broker to connect and perform operations.

To connect to an MQTT broker, we need to create an MQTT client instance. We can do this using the mqtt package we just installed.

1
2
import mqtt from 'mqtt';
const client = mqtt.connect('mqtt://broker.hivemq.com');

In the above example, we create an MQTT client instance and connect it to a broker with the address mqtt://broker.hivemq.com.

Connect using Credentials

The MQTT.js package supports authentication when connecting to an MQTT broker.

You’ll need this if you are using HiveMQ cloud. You can generate and retrieve your credentials from your dashboard.

Here’s an example of how to connect with authentication:

1
2
3
4
5
6
import mqtt from 'mqtt';

const client = mqtt.connect('mqtt://broker.hivemq.com', { 
  username: 'my-username',
  password: 'my-password'
});

We create an MQTT client instance above and connect to the HiveMQ broker with the address mqtt://broker.hivemq.com. We also specify the username and password options to authenticate the client with the broker.

Authentication such as this is often used to secure networks of clients and limit access to guarded data.

Connect with a client SSL Certificate

You can also connect to an MQTT broker with SSL/TLS encryption and client-side SSL/TLS authentication.

Here’s an example of how to connect to an MQTT broker with SSL certificates:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
import fs from 'fs';
import mqtt from 'mqtt';

const options = {
  protocol: 'mqtts',
  host: 'broker.hivemq.com',
  port: 8883,
  ca: [fs.readFileSync('/path/to/ca.crt')],
  cert: fs.readFileSync('/path/to/client.crt'),
  key: fs.readFileSync('/path/to/client.key'),
};

const client = mqtt.connect(options);

In this example, we specify the following options:

  • protocol: the protocol to use (mqtts for SSL/TLS encryption)
  • host: the hostname of the broker
  • port: the port number to connect to (usually 8883 for SSL/TLS encryption)
  • ca: an array of trusted CA certificates
  • cert: the client certificate
  • key: the client private key

We are also using the fs module to read the SSL certificates from the file system.

SSL/TLS encryption and client-side SSL/TLS authentication allow us to securely connect to an MQTT broker and protect our MQTT data from eavesdropping and tampering.

MQTT Extended Authentication

Extended authentication in MQTT version 5 provides additional authentication options beyond the standard username and password.

See this helpful video to learn more about MQTT 5 Enhanced Authentication:

One example of a typical extended authentication implementation is using certificates:

MQTT.js supports client-side SSL/TLS authentication, which can be used to authenticate clients with certificates. Here’s an example of using certificates for extended authentication:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
import fs from 'fs';
import mqtt from 'mqtt';

const options = {
  protocolVersion: 5,
  ca: [fs.readFileSync('/path/to/ca.crt')],
  cert: fs.readFileSync('/path/to/client.crt'),
  key: fs.readFileSync('/path/to/client.key')
};

const client = mqtt.connect('mqtt://broker.hivemq.com', options);

client.on('auth', (packet, cb) => {
  console.log('Authenticating with certificate...');

  // Check the certificate properties and perform the authentication logic here.
  // Call cb() with an error if authentication fails or null if it succeeds.
  cb(null);
});
  

In this example, we specify the client-side SSL/TLS certificates for authentication and listen for the auth event of the MQTT client instance to perform the additional authentication logic in the event listener.

Extended authentication provides flexible and secure authentication options for MQTT applications, helping to prevent unauthorized access to MQTT data.

More information on MQTT authentication & even authorization is available:

Connect over Websockets

Websockets are helpful partly because they can run over HTTP ports if needed. It’s a protocol for two-way communication between a client and a server over a single, long-lived, bi-directional TCP connection. Unlike traditional HTTP requests, which are unidirectional, a WebSocket connection enables real-time communication between the client and server, allowing instant updates and notifications.

MQTT can use Websockets as a transport, so we can connect using a Websocket.

Need to get past a firewall? Configure your client and broker to connect Websocket c over port 80 or 443.

To make a WebSocket connection using the MQTT.js library, specify the ws protocol in the MQTT broker URL when creating the MQTT client instance.

1
const client = mqtt.connect('ws://broker.hivemq.com:8000');

Modern web browsers widely support WebSocket connections and are available as a standard feature in many programming languages and frameworks.

A Note on TCP Ports Used for MQTT

The two most commonly used ports for MQTT are 1883 and 8883.

Port 1883 is the default unencrypted port for MQTT and is usually used for communication over a local network or a secure network, such as a VPN.

Port 8883 is the default port for MQTT over SSL/TLS (Secure Sockets Layer/Transport Layer Security). It is used for communication over the internet or other untrusted networks where security is a concern.

The following table illustrates a few common default ports for various connect strategies. These ports can be configured to use different values depending on the specific MQTT implementation or use case. Check with your broker to be sure which ports are open for which functionality.

Note that TCP ports 8883 and 1883 are registered with IANA for MQTT TLS and non-TLS communication respectively.

Port NumberProtocolDescription
1883TCPDefault unencrypted port for MQTT communication
8000TCPPort for MQTT over Unsecured WebSocket protocol
8080TCPPort for MQTT over WebSocket protocol
8883TCPDefault port for MQTT over SSL/TLS encryption and auth
8884TCPDefault port for MQTT over Websocket with SSL/TLS encryption

Ports can vary from broker to broker, so check the documentation to see which ports your broker offers.

Arguments & Options

For a complete list and description of arguments & options for the connect operation, see the MQTT.js repository documentation.

Subscribing to an MQTT Topic

We can use the subscribe method of the MQTT client instance to subscribe to an MQTT topic.client.subscribe('ma/boston/sensors/tower1');

This simply subscribes to the ma/boston/sensors/tower1 topic.

Topics are key protagonists in MQTT. For more information on topics, see MQTT Topics, Wildcards, & Best Practices - MQTT Essentials: Part 5

Specifying a Message Handler

Making a subscription is great, but how do we execute code when receiving a message? Setup a handler:

1
2
3
client.on('message', (topic, message) => {
  console.log(`Received message on topic ${topic}: ${message}`);
});

Arguments & Options

Parameters

ParameterDescription
topicA string or array topic to subscribe to. It can also be an object, it has as object keys the topic name and as value the QoS, like {'test1': {qos: 0}, 'test2': {qos: 1}}. MQTT topic wildcard characters are supported (+ - for single level and # - for multi-level)
optionsThe options to subscribe with
callbackFunction (err, granted) called on suback where err is a subscription error or an error that occurs when a client is disconnected. 
granted is an array of {topic, qos} where topic is a subscribed to topic and qos is the granted QoS level on it.

Options

PropertyDescription
qosQoS subscription level, default 0
nlNo Local MQTT 5.0 flag (If the value is true, Application Messages MUST NOT be forwarded to a connection with a ClientID equal to the ClientID of the publishing connection)
rapRetain as Published MQTT 5.0 flag (If true, Application Messages forwarded using this subscription keep the RETAIN flag they were published with. If false, Application Messages forwarded using this subscription have the RETAIN flag set to 0.)
rhRetain Handling MQTT 5.0 (This option specifies whether retained messages are sent when the subscription is established.)
propertiesMQTT 5.0 properties object
 - subscriptionIdentifier: representing the identifier of the subscription, number
 - userProperties: The User Property is allowed to appear multiple times to represent multiple name, value pairs, object

Publishing a message

To publish a message to an MQTT topic, we can use the publish method of the MQTT client instance.

1
client.publish('cluster/messages/node7', 'Hello, HiveMQ!');

Here we publish a simple string to the cluster/messages/node7 topic.

Using the Retain Flag

Setting the MQTT retain option to true means that the most recent message with the same topic will be stored on the broker and sent to new subscribers when they connect to the topic. This is useful for providing initial state or configuration data to new subscribers.

Here’s how to send an MQTT message and notify the MQTT broker to “retain” that message for all new subscribers:

1
2
3
4
5
6
7
client.publish('cluster/messages/node7', 'Hello, HiveMQ!', { retain: true }, (err) => {
  if (err) {
    console.error('Failed to publish message:', err);
  } else {
    console.log('Message published with retain flag set to true');
  }
});

User Properties

With MQTT v5 you can send User Properties, similar to HTTP headers. User properties are optional key-value pairs that can be added to MQTT messages. They can be used to provide additional context or metadata about the message.

1
2
3
4
5
6
7
client.publish('cluster/messages/node7', 'Hello, HiveMQ!', { properties: { userProperties: { 'Source-Sensor-ID': '2dfxby20v2.1hz;vg' } } }, (err) => {
  if (err) {
    console.error('Failed to publish message:', err);
  } else {
    console.log('Message published with user properties');
  }
});

Arguments & Options Reference

This is the entire list of arguments & options for the publish operation.

Parameters

ParameterDescription
topicThe topic to publish to, String
messageThe message to publish, Buffer or String
optionsThe options to publish with
callbackfunction (err)

Options

Where options can be:

OptionDescription
qosQoS level, Number, default 0
retainRetain flag, Boolean, default false
dupMark as duplicate flag, Boolean, default false
propertiesMQTT 5.0 properties object
cbStorePutfunction ()

Properties

and properties can be:

PropertyDescription
payloadFormatIndicatorPayload is UTF-8 Encoded Character Data or not boolean
messageExpiryIntervalThe lifetime of the Application Message in seconds, number
topicAliasValue that is used to identify the Topic instead of using the Topic Name, number
responseTopicString which is used as the Topic Name for a response message, string
correlationDataUsed by the sender of the Request Message to identify which request the Response Message is for when it is received, binary
userPropertiesThe User Property is allowed to appear multiple times to represent multiple name, value pairs, object
subscriptionIdentifierRepresenting the identifier of the subscription, number
contentTypeString describing the content of the Application Message, string

Events in MQTT.js

The MQTT.js package provides several event callbacks to handle various events in an MQTT client’s lifecycle. Here are some of the most commonly used events in the MQTT.js package and what they are used for:

  1. connect - The connect event is emitted when the MQTT client successfully connects to the broker. This event can be used to perform actions that depend on the client being connected, such as subscribing to topics or publishing messages. A connack object representing the received CONNACK packet is passed to the event handler.
  2. reconnect - The reconnect event is emitted when the MQTT client tries to reconnect to the broker after a connection loss. This event can be used to perform actions that depend on the client being reconnected, such as resubscribing to topics or republishing messages.
  3. disconnect - The disconnect event in the MQTT.js package is emitted when the MQTT client disconnects from the broker for any reason, whether it’s due to an intentional disconnection initiated by the client or an unexpected disconnection due to network issues, broker unavailability, or other reasons.
  4. end - The end event is emitted when mqtt.Client#end() is called. If a callback was passed to mqtt.Client#end(), this event is emitted once the callback returns.
  5. packetsend - The packetsend event is emitted each time the client sends any MQTT packet. The sent packet as packet will be passed to the handler.
  6. packetreceive - The packetreceive event is emitted each time the client receives any MQTT packet. The received packet as packet will be passed to the handler.
  7. message - The message event is emitted when the MQTT client receives a message from the broker. This event can be used to handle incoming messages and perform actions based on the content of the message, such as updating a user interface or triggering a process. The handler for this event will receive three arguments: topic: the topic of the received message, message: the payload of the received message and packet: the received MQTT packet.
  8. error - The error event is emitted when an error occurs in the MQTT client, such as a connection error or a parsing error. This event can be used to handle errors and perform actions based on the type and severity of the error. The handler for this event may receive one of the following TLS errors: ECONNREFUSED, ECONNRESET, EADDRINUSE or ENOTFOUND.
  9. offline - The offline event is emitted when the MQTT client is disconnected from the broker and cannot reconnect automatically. This event can be used to perform actions that depend on the client being offline, such as stopping processes or sending alerts.
  10. close - The close event is emitted when the MQTT client’s connection to the broker is closed, intentionally or due to an error. This event can be used to perform actions that depend on the client being disconnected, such as cleaning up resources or logging data.

Note: For information on handling and working with MQTT packets directly for some of the events above, see the “Dealing with MQTT Packets” section below.

The event callback system in MQTT.js allows for creating robust and flexible MQTT applications that can handle a wide range of scenarios and requirements.

Example Usage

A few examples to illustrate how to utilize events in your code:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
client.on('offline', () => {
  console.log('Client is offline');
});

client.on('reconnect', () => {
  console.log('Reconnecting to MQTT broker');
});

client.on('end', () => { 
  console.log('Connection to MQTT broker ended');
});

Reconnecting

Sometimes, the connection to the MQTT broker may be lost due to network issues or other reasons.

MQTT is exceptionally well adapted to manage sessions across disconnections. For more information, see Persistent Session and Queuing Messages - MQTT Essentials: Part 7.

This section explains how the MQTT.js client reconnects in case of loss of connection.

By default, the MQTT.js client will retry connections every 1000ms. See the next section on how to configure this.

When connections are lost, they will be automatically retried. We can use the reconnect event of the MQTT client instance to perform any related tasks. These tasks might include republishing a message or resubscribing to a topic.

1
2
3
client.on('reconnect', () => {
  console.log('Reconnecting...');
});

Reconnect Period

The reconnectPeriod option in the MQTT.js package specifies the time interval in milliseconds between reconnection attempts when the connection to the MQTT broker is lost. Here’s an example of how to use the reconnectPeriod option:

1
2
3
4
5
import mqtt from 'mqtt';

const client = mqtt.connect('mqtt://broker.hivemq.com', {
  reconnectPeriod: 5000
});

Quality of Service levels

MQTT supports three different Quality of Service (QoS) levels:

  • QoS 0: At most once delivery
  • QoS 1: At least once delivery
  • QoS 2: Exactly once delivery

These levels are explained in more detail in MQTT Quality of Service (QoS) 0,1, & 2 – MQTT Essentials: Part 6.

To specify the QoS level when publishing a message, we can pass an object as the third argument to the publish method with the qos property set to the desired QoS level.

1
client.publish('location/gps/vehicle1', 'Hello, HiveMQ!', { qos: 1 });

In the above example, we are publishing the message 'Hello, HiveMQ!' to the topic location/gps/vehicle1 with a QoS level of 1.

Learn more about MQTT Quality of Service Levels in MQTT Quality of Service (QoS) 0,1, & 2 – MQTT Essentials: Part 6.

Last Will and Testament

MQTT supports the Last Will and Testament (LWT) feature, which allows a client to specify a message to be sent by the broker if the client disconnects unexpectedly.

To learn more about the Last Will and Testament feature of MQTT, see Last Will and Testament - MQTT Essentials: Part 9.

To specify the LWT message, we can use the will option when creating the MQTT client instance.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
import mqtt from 'mqtt';

const client = mqtt.connect('mqtt://broker.hivemq.com', {
  will: {
    topic: 'location/gps/vehicle1',
    payload: 'Goodbye, HiveMQ!',
    qos: 1,
    retain: true
  }
});

The LWT message in the above example will be sent to the topic location/gps/vehicle with a payload of 'Goodbye, HiveMQ!', a QoS level of 1, and the retain option set to true.

Learn more about MQTT Last Will & Testament in Last Will and Testament - MQTT Essentials: Part 9.

Topic Alias Management

Topic alias management is a feature introduced in MQTT version 5 that allows clients to use a short numeric identifier (alias) instead of a full topic string in publish and subscribe messages. This can reduce the size of MQTT packets and improve network performance.

The MQTT.js package supports topic alias management in MQTT version 5, and it also provides an option autoUseTopicAlias to automatically use topic aliases when possible. Here’s an example of how to use topic aliases with autoUseTopicAlias:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
import mqtt from 'mqtt';

const client = mqtt.connect('mqtt://broker.hivemq.com', {
  protocolVersion: 5,
  autoUseTopicAlias: true
});

client.subscribe('location/gps/vehicle1');

// Publish a message and assign a alias to the topic (1)
client.publish('location/gps/vehicle1', 'Hello, HiveMQ!', { topicAlias: 1 });

// On the next publish, the topic alias value will be used (instead of the entire topic string) when communicating with the broker
client.publish('location/gps/vehicle1', 'Still here, HiveMQ!');

The first call to publish registers the topic alias. And even though we specified the topic again in the second publish call, internally, the MQTT.js package will use the topic alias in the actual packet sent on the wire.

By using autoUseTopicAlias, we can improve network performance by automatically using topic aliases when possible. However, it’s important to note that topic aliases should only be used when both the client and the broker support MQTT version 5 and topic alias management.

Of course, the HiveMQ MQTT broker fully supports this and all of the other advanced features of the MQTT specification.

Another related option is autoAssignTopicAlias. It is used to automatically assign a topic alias to a published message if the topic name has been previously used in a message. Like the previous option, this also reduces the size of MQTT messages by replacing the topic name with a shorter alias - except automatically:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
import mqtt from 'mqtt';

// Connect to the MQTT broker
const client = mqtt.connect('mqtt://localhost', {
  autoUseTopicAlias: true,
  autoAssignTopicAlias: true
});

// Publish a message with a topic name
client.publish('location/gps/vehicle1', 'Hello, HiveMQ!');

// Publish another message with the same topic name
// This time a topic alias will be automatically used in
// the MQTT packet instead of the full topic name
client.publish('location/gps/vehicle1', 'Still here, HiveMQ!');

// Disconnect from the broker
client.end();

In short, MQTT Topic Alias is an efficiency and performance feature of MQTT. If possible, it should be enabled and utilized to lower the network burden of transmitting long topic names repeatedly.

Closing the Connection

To close an MQTT connection using the MQTT.js library, you can call the end() method on the MQTT client object.

Here’s an example:

1
2
// Close the connection
client.end();

In some cases, it may be necessary to immediately terminate the connection without waiting for in-flight messages to be acknowledged, for example, if the connection is no longer needed and needs to be closed quickly.

To do this, you can call the end() method with the force argument set to true, like this:

1
client.end(true);

And there are more options. In all, the end call accepts three parameters:

OptionTypeDescription
forceBooleanWhen set to true, it immediately terminates the underlying network connection without waiting for any in-flight messages to be acknowledged. Can result in lost or inconsistent data if there are any in-flight messages.
optionsObjectThe options for the call.
callbackObjectThe code to execute when the client is closed.

The possible options are:

OptionTypeDescription
reasonCodeIntegerRepresents the reason code for the DISCONNECT message sent to the broker when closing the connection. Can be used to provide additional information about the reason for disconnecting, such as an error code or status. See the MQTT Specification for a list of Reason Codes.
propertiesObjectContains additional properties to include in the DISCONNECT message sent to the broker when closing the connection. Can be used to provide additional metadata or context about the disconnect, such as the client identifier or session expiry.

And for properties:

Property nameTypeDescription
sessionExpiryIntervalNumberSpecifies a new Session Expiry Interval for the broker.
reasonStringStringProvides additional context or information about the reason for disconnecting.
userPropertiesObjectContains additional user-defined properties to include in the message.

Close the Connection with a Reason Code

As an example, if we wanted to disconnect gracefully but still have the Last Will & Testament message sent:

1
2
3
4
5
6
7
// Close the connection with reason code
client.end({
  reasonCode: 0x04, // Disconnect with Will Message
  properties: {
    reasonString: 'Closing connection with Will Message'
  }
});

Note: Get the list of valid Reason Code values directly from the MQTT v5 Specification

Learn more about MQTT Last Will & Testament in Last Will and Testament - MQTT Essentials: Part 9.

Close the Connection and Update the Session Expiry Interval

To send an updated Session Expiry Interval when disconnecting:

1
2
3
4
5
6
// Close the connection with reason code
client.end({
  properties: {
    sessionExpiryInterval: 90
  }
});

Learn more about MQTT Session Expiry Intervals in Session and Message Expiry Intervals - MQTT 5 Essentials Part 4.

Dealing with MQTT Packets

Internally, the MQTT.js package doesn’t generate or parse any MQTT packets. That functionality lives in another package: mqtt-packet.

The mqtt-packet package is a standalone Node.js package that offers low-level MQTT packet parsing and serialization functionality. It is used internally by the MQTT.js package to encode and decode MQTT packets.

It provides methods for encoding MQTT packets into binary data and decoding binary data into MQTT packets. It also provides a set of constants for MQTT packet types, packet flags, QoS levels, and error codes.

If you have to work with MQTT packets directly, such as in the case of some of the MQTT.js events discussed above, see the mqtt-packet package for details on packet properties and abilities.

The source code for the mqtt-packet package with a good amount of documentation is available on Github.

Promises and async-mqtt

Promises were introduced into Node.js in version 0.12 in 2015. Promises are objects that represent the eventual completion (or failure) of an asynchronous operation and allow us to chain multiple asynchronous operations together in a more structured way.

The async-mqtt package is a wrapper around the MQTT.js library that adds support for promises making it easy to work with asynchronous operations in an MQTT application.

Here’s an example of how to use promises in the async-mqtt package:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
import mqtt from 'async-mqtt';

const client = mqtt.connect('mqtt://broker.hivemq.com');

client.on('connect', async () => {
  console.log('Connected to the Hive!');

  try {
    const result = await client.publish('location/gps/vehicle1', 'Hello, HiveMQ!');
    console.log('Message published:', result);
  } catch (error) {
    console.error('Error publishing message:', error);
  }
});

Note the use of the async keyword to make the on event handler function asynchronously and use the await keyword to wait for the completion of the publish operation.

Error Handling

Errors can occur in a variety of situations, such as when connecting to a broker, publishing or subscribing to messages, or when the network connection is lost. Implementing proper error handling in your code is crucial to ensure the reliable operation of your MQTT client application and effectively manage any errors that may occur.

There are two general ways to handle errors with the MQTT.js package.

  1. The global error event is emitted when an error occurs in the MQTT client, such as a connection error or a parsing error.
1
2
3
client.on('error', (err) => {
  console.error('Error connecting to MQTT broker:', err);
});
  1. Per operation error handlers
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
// Publish
client.publish('location/gps/vehicle1', 'Hello, HiveMQ!', (err) => {
  if (err) {
    console.error('Error publishing message:', err);
  } else {
    console.log('Message published successfully');
  }
});

// Subscribe

client.subscribe('location/gps/vehicle1', { qos: 1 }, (err) => {
  if (err) {
    console.error('Error subscribing to topic:', err);
  } else {
    console.log('Subscribed to topic successfully');
  }
});

// etc...

Error handling is crucial to developing reliable MQTT client applications using the MQTT.js package. Handling errors allows the application to respond appropriately to various scenarios that can cause the MQTT client to fail or lose connectivity to the broker.

By properly handling errors, applications operate more reliably, predictably, and helps in diagnosis when problems arise.

Debug Logging

To enable debug logging in the MQTT.js package, simply set the environment variable DEBUG=mqttjs\*.

CLI Support

The MQTT.js package includes a command line interface (CLI) tool named mqtt that provides a convenient way to interact with MQTT brokers and perform various operations, such as publishing and subscribing to topics, using the command line. This is an excellent option to assist in the development, testing, and debugging of problems.

To get it, install the MQTT.js package globally using the npm package manager. You can do this by running the following command in your terminal:

1
npm install -g mqtt

From there, you can then subscribe to topics, publish messages and more. Try the following two commands in different shell sessions:

1
2
3
mqtt sub -t 'cluster/messages/node7' -h 'broker.hivemq.com' -v

mqtt pub -t 'cluster/messages/node7' -m "Hello, HiveMQ!" -h 'broker.hivemq.com'

Designing and Implementing MQTT Payloads

Now that you know about the MQTT operations, what do you put in the payload? Will the receiving end understand and be able to decode the data correctly? This is a problem beyond the scope of MQTT, which just provides the transport.

Designing and implementing MQTT payloads involves several considerations, including the size, format, and encoding of the payload data. Here are some guidelines for designing and implementing MQTT payloads:

  1. Size - MQTT payloads should be as small as possible to reduce network traffic and improve performance. Consider the size of the data you want to send and try to minimize it when possible.
  2. Format - MQTT payloads can be formatted in several ways, including JSON, XML, or even binary. The choice of format depends on the requirements of the application and the capabilities of the MQTT client and broker.
  3. Encoding - MQTT payloads should be encoded in a format supported by both the MQTT client and the receiver. Common encodings include UTF-8, ASCII, and binary formats.
  4. Topic structure - MQTT topics should be structured in a way that is easy to understand and maintain. Consider using a hierarchical topic structure that is easy to navigate and organize.
  5. QoS level - MQTT payloads can be published with different QoS levels, which determine the reliability and delivery guarantees of the message. Consider the QoS level of the message when designing the payload and ensure that it is appropriate for the application’s requirements.

Here’s an example of how to design and implement an MQTT payload in JSON format:

1
2
3
4
5
{
  "temperature": 32.0,
  "humidity": 68.0,
  "sensor-id": "ma7-kuj-8.x"
}

This JSON payload is small and easy to read and can be easily parsed and processed by another MQTT client that supports JSON decoding.

It’s important to consider the requirements of the application and the capabilities of the MQTT client and broker. The aim is to create efficient and effective MQTT payloads that meet the needs of the MQTT application.

IoT and MQTT Sparkplug

Regarding payloads, the MQTT Sparkplug specification deals precisely with this. It provides a standardized approach to using MQTT in industrial applications and defines a set of guidelines for message payload format, topic structure, and QoS levels.

Industrial IoT applications typically involve complex, large-scale systems with many devices and sensors. These systems often have strict requirements for reliability, scalability, and real-time performance. MQTT provides a flexible and scalable protocol for connecting devices and transmitting data. Without a standardized approach, it can be challenging to ensure compatibility and interoperability between devices and systems. As the number of connected devices grows, so does the complexity if it’s not well managed.

The MQTT Sparkplug specification addresses these obstacles by providing a standardized approach to using MQTT. It defines a set of guidelines for message payload format, topic structure, and QoS levels that ensure compatibility and interoperability between devices and systems.

It also defines a set of data structures and payload formats optimized for industrial IoT applications. These data structures include device and sensor data, alarms, events, and commands. The payload formats are designed to be efficient, compact, and easily parsed by MQTT clients and brokers.

And finally, it defines a set of guidelines for topic structure and QoS levels. The topic structure is hierarchical and follows a standardized naming convention that makes navigating and organizing MQTT topics easy. The QoS levels are carefully chosen to balance reliability and performance and ensure that data is delivered promptly and efficiently.

The MQTT Sparkplug specification provides a standardized approach to using MQTT that ensures compatibility and interoperability between devices and systems.

Best MQTT Broker

Do you need an MQTT broker for your project? HiveMQ Cloud offers a hosted and fully managed broker so you can focus on your applications. It’s free for the first 100 device connections.

Alternatively, you could run the broker locally with docker run --name hivemq-ce -d -p 1883:1883 hivemq/hivemq-ce. The source code to the community edition is on Github.

If you have a larger project, the HiveMQ broker can scale up to 200 million devices!

Conclusion

We have thoroughly explored how to use MQTT in Node.js. We have covered how to connect to an MQTT broker, subscribe to topics, publish messages, reconnect, use Quality of Service levels, use Last Will and Testament and much more.

We at HiveMQ hope you found this guide helpful and wish you the best of luck in your MQTT adventures. If some questions arise along the way, we are always happy to help! Come visit our Community Forums and join us in the MQTT discussion.

If you need a reliable, secure, performant and the best MQTT broker available by far, check out HiveMQ Cloud - we’d be honored to have you as a user.

Resources

Here are some valuable links you are likely to need during the course of development.

Code, Libraries and Packages

Learning

Specifications

Tools

Other HiveMQ MQTT Clients

Help & Support

author Peter Giacomo Lombardo

About Peter Giacomo Lombardo

Peter is a Staff Software Engineer at HiveMQ leading the effort to create best-of-breed MQTT & Sparkplug clients for the great HiveMQ community and beyond.

Follow Peter on LinkedIn and Github

mail icon Contact HiveMQ
newer posts How to Build .Net IoT Apps Using C# MQTT Client
Empowering Digital Transformation in Pharma Manufacturing – Pharma 4.0 older posts