expressify-mqtt
v1.0.3
Published
Transport strategy for Expressify REST mechanisms over MQTT.
Downloads
13
Readme
expressify-mqtt
An Expressify strategy enabling RESTful application over an MQTT(S) transport.
Current version: 1.0.3
Lead Maintainer: Halim Qarroum
Table of contents
Install
npm install --save expressify-mqtt
Features
- Natively supports the MQTT.js and the AWS IoT SDK libraries.
- Usage of an MQTT query-response pattern optimized for minimizing message exchanges and costs.
- Supports observation of resources using dedicated MQTT topics.
- Supports Node.js and the Browser (using MQTT-over-Websockets).
- Automatically detects disconnected observers to stop emitting resource updates.
Usage
In order to use expressify-mqtt
, you need to create an instance of the strategy using a backend such as MQTT.js or the AWS IoT SDK. The strategy requires that the MQTT backend follows the same interface as MQTT.js.
Creating a client
When initializing the expressify-mqtt
strategy, you need to pass it a supported MQTT backend
, as well as a topic mountpoint indicating the base topic which the strategy will use to create its topic architecture.
// Injecting the `mqtt.js` library.
const mqtt = require('mqtt');
// Creating the client instance.
const client = new Expressify.Client({
strategy: new MqttStrategy({
mqtt: backend,
topic: 'my/topic'
})
});
Note that given the used MQTT service, there might be limitations on the number of forward slashes you can use. For example, on AWS IoT the maximum amount of forward slashes is 7 as of today. Also, have a read on how the query-response pattern over MQTT works in
expressify-mqtt
to understand the maximum number of forward slashes you can use on a topic mountpoint.
Creating a server
The below example shows you how to create an instance of an Expressify server using the mqtt
strategy. Similarly to Creating a client, you need to pass to the strategy an MQTT backend
and the MQTT topic mountpoint which must be the same as for the client.
// Injecting the `mqtt.js` library.
const mqtt = require('mqtt');
// Creating the server instance.
const server = new Expressify.Server({
strategy: new MqttStrategy({
mqtt: backend,
topic: 'my/topic'
})
});
// Listening for incoming requests.
server.listen().then(() => {
console.log(`[+] The server is listening on mount point '${server.strategy.opts.topic}' !`);
});
Connecting the back-end
In order for an expressify-mqtt
client or server to be able to start subscribing or publishing on MQTT topics using the backend
instance, it should be configured and connected before using expressify. See the examples available in this repository to see more in details how to connect the back-ends before using either the client or the server.
The configuration object
As you will see it in the examples, The MQTT.js
and the AWS IoT SDK
requires a configuration object to be passed in order to specify the configuration properties (server, certificates, connection parameters, etc.) to use while connecting to an MQTT server.
An example of such a configuration object is available here, replace the placeholders between diamonds with the correct values. The referenced certificate paths (keyPath
, certPath
and caPath
) should be placed in the common/certs
directory for the examples to properly work.
For more information on the values you can put in the config.json
file, please read the associated documentation on the AWS IoT SDK for Javascript.
Implementation details
In this section will be detailed implementation details about the inner workings of the expressify-mqtt
module.
Query-Response pattern
To enable a stateless query-response model over MQTT, this module is based on the establishment of a topic nomenclature which is optimized in terms performance and costs.
When issuing a request or a response, expressify-mqtt
builds its query and response topics on top of the topic mountpoint documented in the previous sections and uses it as a mounting point for the rest of the topic schema.
Anatomy of a transaction
When an expressify-mqtt
client queries a remote server, it will transmit the standard expressify query payload over the following topic :
${"topic-mount-point"}/${"query-transaction-id"}/request
Where topic-mount-point
is the topic mountpoint given by the user of the strategy when instantiating it,query-transaction-id
is a UUID v4 generated per query, and request
is a string literal indicating the type of the transaction.
Before sending this request, the expressify client will subscribe to the following topic (the same nomenclature as for the request is used for variable names) :
${"topic-mount-point"}/${"query-transaction-id"}/response
When the server responds to the request, it will send it on the above MQTT topic using the same transaction identifier as for the request.
Anatomy of event notifications
Since Expressify offers the ability for clients to observe a remote resource on the server, and be notified whenever this resource has been modified, the expressify-mqtt
strategy bases the event notification flow upon the following topic model.
When a client wishes to subscribe to a remote resource, it sends a request (as for a regular request) to the server indicating the resource it would like to subscribe to.
Once acknowledge by the server, subsequent updates and notifications associated with the observed resource will be sent by the server on the below topic.
${"topic-mount-point"}/${"resource-name"}/events
Message ordering
By default, the expressify-topic
when publishing a message on an MQTT broker will do so using a QoS
of 1 to avoid lost messages. This can be overriden by the user of the module when instanciating the strategy :
// Using a `QoS` of `0`.
new MqttStrategy({
mqtt: backend,
topic: 'my/topic',
mqttOpts: { qos: 0 }
})
Unless the used MQTT broker supports QoS 2
, there is a chance that your messages will not arrive in the same order they have been sent initially by the client. Since some applications might require message ordering to be guaranteed, it is possible to activate this feature on expressify-mqtt
for events, but not for query-responses since those are supposed to be stateless.
Below is an example of how to send events in an ordered manner from a server instance when notifying the client about a change on one of the resource it exposes.
server.publish(`/my/resource`, { foo: 'bar' }, { ordered: true });
A sequence number will be sent by the server on each
.publish()
to allow the client to re-order the messages. Note that the client will wait for a short period of time to receive an expected message after having received subsequent messages. Once the timeout has been reached, the message is considered lost, andexpressify-mqtt
will continue ordering subsequent messages to avoid the client to be trapped into a 'hanged' situation.
Examples
Two functional examples involving the expressify-mqtt
strategy are available in the examples directory :
- Remote storage - Demonstrates how to use
expressify-mqtt
to expose a REST interface on the server which can store in memory a set of key-value pairs, and on the client on how to query this service remotely over MQTT. - System monitoring - Shows you how to use
expressify-mqtt
to expose system metrics on the server and to display them to the user on the client.
See also
- The Expressify framework.
- The expressify-ipc strategy supporting local sockets.
- The expressify-pm strategy supporting
.postMessage
.