MQTT.js est le client MQTT de référence pour JavaScript, et utiliser MQTT.js avec Node.js est le moyen le plus rapide de construire des backends IoT, des passerelles et des outils de test. Dans ce tutoriel, vous allez créer un publisher et un subscriber Node.js avec MQTT.js v5, utiliser son API à base de promesses, choisir les niveaux de QoS, ajouter un Last Will, passer à TLS et configurer la reconnexion pour un service de longue durée. Tous les exemples tournent sur le serveur MQTT public gratuit broker.connectmq.com : il vous suffit d'avoir Node.js installé.
Prérequis
- Node.js 18 ou plus récent (
node --version) - npm, pnpm ou yarn
- Facultatif :
mosquitto_subpour observer le trafic depuis un second terminal
Créez un projet et installez la bibliothèque :
mkdir mqtt-node-demo && cd mqtt-node-demo
npm init -y
npm pkg set type=module
npm install mqtt
Définir "type": "module" vous permet d'utiliser la syntaxe import et le await de premier niveau. MQTT.js v5 est écrit en TypeScript et embarque ses propres définitions de types : les éditeurs proposent donc l'autocomplétion sans paquet supplémentaire.
URL de connexion et ports
MQTT.js choisit le transport d'après le schéma de l'URL. Sur les brokers ConnectMQ, les ports sont les suivants :
| Schéma | Port | Transport | Usage typique en Node.js |
|---|---|---|---|
mqtt:// |
1883 | TCP en clair | Tests locaux uniquement |
mqtts:// |
8883 | TCP avec TLS | Services de production |
ws:// |
8083 | WebSocket | Rarement utile en Node.js |
wss:// |
8084 (chemin /mqtt) |
WebSocket sécurisé | Navigateurs, proxys restrictifs |
En Node.js, privilégiez mqtts:// sur le port 8883. Le WebSocket compte surtout pour les navigateurs, qui ne peuvent pas ouvrir de sockets TCP bruts ; l'article MQTT over WebSocket traite de cet aspect.
Votre premier subscriber MQTT.js
Enregistrez sous subscriber.js :
import mqtt from "mqtt";
const PREFIX = "connectmq/demo/node-a71c";
const clientId = `node-sub-${Math.random().toString(16).slice(2, 10)}`;
const client = mqtt.connect("mqtts://broker.connectmq.com:8883", {
clientId,
protocolVersion: 5,
clean: true,
keepalive: 30,
reconnectPeriod: 2000, // attendre 2 s entre deux tentatives de reconnexion
connectTimeout: 10000, // abandonner une tentative après 10 s
});
client.on("connect", async (connack) => {
console.log(`Connected as ${clientId}, session present: ${connack.sessionPresent}`);
const granted = await client.subscribeAsync(`${PREFIX}/+/telemetry`, { qos: 1 });
console.log("Subscribed:", granted.map((g) => `${g.topic} (qos ${g.qos})`).join(", "));
});
client.on("message", (topic, payload, packet) => {
let data;
try {
data = JSON.parse(payload.toString());
} catch {
data = payload.toString();
}
const device = topic.split("/").at(-2);
console.log(`[${device}] qos=${packet.qos} retain=${packet.retain}`, data);
});
client.on("reconnect", () => console.log("Reconnecting..."));
client.on("offline", () => console.log("Offline"));
client.on("error", (err) => console.error("MQTT error:", err.message));
process.on("SIGINT", async () => {
await client.endAsync();
process.exit(0);
});
S'abonner dans le handler connect est un choix délibéré. Avec clean: true, le broker oublie les abonnements quand la connexion tombe : se réabonner à chaque connexion garantit que vous continuez à recevoir les messages après une reconnexion.
Publier avec QoS et Last Will
Enregistrez sous publisher.js :
import mqtt from "mqtt";
const PREFIX = "connectmq/demo/node-a71c";
const DEVICE = "sensor-01";
const clientId = `node-pub-${Math.random().toString(16).slice(2, 10)}`;
const client = await mqtt.connectAsync("mqtts://broker.connectmq.com:8883", {
clientId,
protocolVersion: 5,
will: {
topic: `${PREFIX}/${DEVICE}/status`,
payload: Buffer.from("offline"),
qos: 1,
retain: true,
},
});
console.log("Connected");
await client.publishAsync(`${PREFIX}/${DEVICE}/status`, "online", { qos: 1, retain: true });
let count = 0;
const timer = setInterval(async () => {
const reading = {
temperature: +(20 + Math.random() * 5).toFixed(2),
humidity: +(40 + Math.random() * 10).toFixed(1),
ts: Date.now(),
};
try {
await client.publishAsync(`${PREFIX}/${DEVICE}/telemetry`, JSON.stringify(reading), {
qos: 1,
properties: { messageExpiryInterval: 60, contentType: "application/json" },
});
console.log("Published", reading);
} catch (err) {
console.error("Publish failed:", err.message);
}
if (++count === 20) {
clearInterval(timer);
await client.publishAsync(`${PREFIX}/${DEVICE}/status`, "offline", { qos: 1, retain: true });
await client.endAsync();
}
}, 2000);
Lancez les deux :
node subscriber.js
# dans un second terminal
node publisher.js
Plusieurs choses se passent ici :
connectAsyncse résout dès que le broker envoie le CONNACK, ce qui est pratique dans les scripts. Dans les services de longue durée, l'événementconnectreste utile car il se déclenche à nouveau après chaque reconnexion.- La QoS 1 signifie que le broker accuse réception de chaque publication, et
publishAsyncne se résout qu'après cet accusé. Pour de la télémétrie à haut débit où une perte occasionnelle est acceptable, la QoS 0 coûte moins cher. Les compromis sont détaillés dans les niveaux de QoS MQTT expliqués. - Le Last Will est enregistré par le broker à la connexion et publié si le client disparaît sans déconnexion propre, afin que les autres services apprennent que le capteur est hors ligne. Le rendre retained permet aux nouveaux abonnés de voir immédiatement le statut actuel.
- Les propriétés MQTT 5 comme
messageExpiryIntervaldemandent au broker d'abandonner le message s'il ne peut pas être livré dans les 60 secondes, ce qui évite l'accumulation de mesures périmées pour les abonnés hors ligne.
L'espace public du serveur MQTT public gratuit est lisible par tout le monde : remplacez la partie node-a71c du préfixe par votre propre chaîne aléatoire et n'y envoyez jamais de données réelles.
Les options de reconnexion qui comptent
MQTT.js se reconnecte automatiquement. Ces options déterminent son comportement :
| Option | Valeur par défaut | Recommandation |
|---|---|---|
reconnectPeriod |
1000 ms | 2000 à 5000 ms pour les services ; 0 désactive la reconnexion |
connectTimeout |
30000 ms | 10000 ms pour détecter plus vite un réseau mort |
keepalive |
60 s | 30 à 60 s ; une valeur plus basse détecte plus tôt les coupures silencieuses |
clean |
true |
false avec un client ID stable si vous avez besoin des messages en file d'attente |
properties.sessionExpiryInterval |
0 | Secondes pendant lesquelles le broker conserve la session après déconnexion (MQTT 5) |
queueQoSZero |
true |
Mettez false si les messages QoS 0 périmés ne doivent pas être envoyés après reconnexion |
Pour un service qui ne doit manquer aucun message pendant une brève absence, combinez un client ID fixe, clean: false, des abonnements en QoS 1 et un intervalle d'expiration de session. Le broker met alors les messages en file d'attente pour la session et les livre au retour du client.
Structurer un service Node.js de longue durée
Les scripts ci-dessus sont volontairement courts. Un service qui tourne pendant des mois a besoin d'un peu plus de structure, et l'essentiel concerne le cycle de vie plutôt que MQTT lui-même :
- Créez un seul client par processus et partagez-le via un module. Ouvrir une nouvelle connexion par requête ou par tâche gaspille des ressources et atteint vite les limites de connexions.
- Considérez
connectcomme le signal « prêt ». Conditionnez le travail qui nécessite le broker à la propriétéconnecteddu client, et décidez quoi faire quand elle vaut false : mettre en tampon, abandonner ou renvoyer une erreur à l'appelant. - Gérez les signaux d'arrêt. Sur
SIGTERM(envoyé par Docker, Kubernetes et systemd), publiez un statut final si vous en utilisez un, appelezendAsync()pour que les messages QoS 1 en cours soient vidés, puis seulement quittez. - Journalisez les transitions d'état, pas chaque message. Les événements
connect,reconnect,offline,closeeterrorsont ceux dont vous avez besoin pour diagnostiquer une panne. Des logs par message les noient. - Gardez des handlers rapides. L'événement
messages'exécute sur l'event loop de Node. Les traitements lourds, comme les écritures en base, doivent être attendus avec soin ou placés dans une file afin qu'un système aval lent ne retarde pas tous les autres messages.
Ces habitudes comptent davantage que la valeur de n'importe quelle option, et elles s'appliquent aussi bien aux passerelles, aux workers qu'aux outils de test construits sur MQTT.js.
S'authentifier sur un broker privé
En production, utilisez les identifiants de votre broker. Avec ConnectMQ, vous créez un broker dans le portail et recevez un nom d'utilisateur, un mot de passe et un client ID. Chargez-les depuis des variables d'environnement :
import mqtt from "mqtt";
const client = mqtt.connect(`mqtts://${process.env.MQTT_HOST}:8883`, {
clientId: process.env.MQTT_CLIENT_ID, // YOUR_CLIENT_ID depuis le portail
username: process.env.MQTT_USERNAME, // YOUR_USERNAME
password: process.env.MQTT_PASSWORD, // YOUR_PASSWORD
protocolVersion: 5,
clean: false,
properties: { sessionExpiryInterval: 3600 },
});
client.on("connect", () => console.log("Connected to private broker"));
client.on("error", (err) => console.error(err.message));
ConnectMQ utilise un certificat Let's Encrypt, que Node.js reconnaît nativement : vous n'avez pas besoin de fournir de fichier CA. Ne définissez jamais rejectUnauthorized: false en production ; cela désactive la vérification des certificats et rend TLS inutile.
Dépannage de MQTT.js sous Node.js
- Connexion, puis déconnexion toutes les quelques secondes. Un autre client utilise le même client ID. Chaque processus en cours d'exécution a besoin de son propre ID.
ERR_REQUIRE_ESMouCannot use import statement. Définissez"type": "module"danspackage.json, ou utilisezconst mqtt = require("mqtt")dans les fichiers CommonJS.- Plus aucun message après une coupure réseau. Vous ne vous êtes abonné qu'une seule fois, en dehors du handler
connect, tout en utilisantclean: true. connack timeout. Un pare-feu bloque le port 8883. Testez avecmosquitto_sub -h broker.connectmq.com -p 8883 --capath /etc/ssl/certs -i test-1 -t testou essayezwss://sur le port 8084.- Abonnement accordé avec une QoS 128 ou un reason code de 0x80 ou plus. Le broker a refusé l'abonnement, souvent à cause d'un filtre invalide ou de la limite d'abonnements du serveur MQTT public gratuit (10 par client).
- La mémoire augmente hors ligne. Les messages sortants sont mis en file jusqu'à la reconnexion. Plafonnez votre propre tampon ou utilisez la QoS 0 avec
queueQoSZero: falsepour la télémétrie jetable.
Étapes suivantes
À partir de là, vous pouvez réutiliser le même client dans un frontend : React avec MQTT construit un tableau de bord à base de hooks sur la version navigateur de MQTT.js, et MQTT typé avec TypeScript ajoute des topics typés et des payloads validés par-dessus ce code. Pour les autres environnements d'exécution, le guide des bibliothèques clientes MQTT compare les principales options. La liste complète des options se trouve dans le README de MQTT.js.
Démarrer avec ConnectMQ
Faites passer vos services Node.js du serveur MQTT public gratuit à un broker privé en quelques minutes. Inscrivez-vous sur app.connectmq.com, choisissez l'offre Serverless gratuite sans carte bancaire, créez un broker et placez les identifiants générés dans votre environnement. Avec un broker MQTT managé, ConnectMQ gère les endpoints TLS et l'infrastructure du broker pendant que vous vous concentrez sur le code de votre application.
Questions fréquentes
MQTT.js v5 est-il compatible avec du code plus ancien ?
En grande partie. L'API à callbacks fonctionne toujours, et la v5 ajoute des méthodes à base de promesses comme connectAsync, publishAsync, subscribeAsync et endAsync. Les principaux changements sont la réécriture en TypeScript et une version minimale de Node.js plus récente.
Faut-il utiliser MQTT 3.1.1 ou MQTT 5 avec MQTT.js ?
Utilisez protocolVersion: 5 lorsque votre broker le prend en charge, comme c'est le cas de ConnectMQ. Vous bénéficiez des reason codes, de l'expiration des messages et des sessions et des user properties, tandis que le code de publication et d'abonnement reste identique.
Combien de connexions un service Node.js doit-il ouvrir ?
Généralement une seule. Un unique client MQTT.js peut publier et s'abonner à de nombreux topics en parallèle, et chaque connexion supplémentaire nécessite son propre client ID unique et compte dans la limite de connexions de votre offre.
Peut-on utiliser MQTT.js avec Bun ou Deno ?
MQTT.js cible Node.js et les navigateurs. Les environnements d'exécution offrant une bonne compatibilité Node.js peuvent souvent le faire tourner, mais testez soigneusement le comportement de TLS et de la reconnexion avant de vous y fier.



