Une passerelle Node-RED HTTP vers MQTT permet à n'importe quel système capable d'envoyer une requête web de déclencher des commandes sur des équipements MQTT, sans ajouter de bibliothèque MQTT à ce système. Dans ce tutoriel, vous allez exposer dans Node-RED un endpoint REST qui valide la requête, publie une commande sur un broker, attend éventuellement la confirmation de l'équipement et renvoie un code de statut HTTP correct.
Quand une passerelle HTTP vers MQTT a du sens
MQTT est le bon protocole entre les équipements et le broker, mais une grande partie des logiciels qui gravitent autour d'une usine ou d'un bâtiment ne connaissent que HTTP :
- Un ERP ou un MES capable d'appeler une URL au démarrage d'un ordre de fabrication.
- Un webhook provenant d'un produit SaaS, d'un prestataire de paiement ou d'un outil de formulaires.
- Des scripts shell et des tâches cron qui utilisent déjà
curl. - Des applications mobiles ou des tableaux de bord auxquels vous ne voulez pas confier des connexions MQTT de longue durée.
- Un intégrateur tiers qui n'accepte qu'un contrat REST.
Au lieu d'apprendre MQTT à chacun de ces systèmes, vous leur fournissez une petite API HTTP bien définie. Node-RED traduit chaque requête en une publication sur le bon topic. Les équipements restent de purs clients MQTT, et le broker reste l'unique endroit par lequel transitent les messages.
Architecture du flow
HTTP client --POST /api/devices/pump1/command--> Node-RED
Node-RED --publish connectmq/demo/h7r2pq/pump1/cmd--> MQTT broker --> device
device --publish connectmq/demo/h7r2pq/pump1/ack--> MQTT broker --> Node-RED
Node-RED --HTTP 200 or 202 or 504--> HTTP client
Nous allons construire trois endpoints :
| Méthode et chemin | Rôle | Réponse |
|---|---|---|
POST /api/devices/:id/command |
Publier une commande et répondre immédiatement | 202 Accepted |
POST /api/devices/:id/command?wait=1 |
Publier et attendre l'acquittement de l'équipement | 200 OK ou 504 Gateway Timeout |
GET /api/devices/:id/state |
Renvoyer le dernier état connu reçu via MQTT | 200 OK ou 404 Not Found |
Pour la démo, nous utilisons le serveur MQTT public gratuit broker.connectmq.com avec le préfixe connectmq/demo/h7r2pq. Choisissez votre propre préfixe aléatoire et n'envoyez jamais de vraies commandes de pilotage via l'espace public : n'importe qui peut y lire et y publier. Il est réservé aux tests et aux prototypes, pas aux données de production.
Étape 1 : l'endpoint HTTP et la validation
Ajoutez un nœud http in avec la méthode POST et l'URL /api/devices/:id/command. Node-RED analyse automatiquement les corps JSON lorsque le client envoie Content-Type: application/json, et place les paramètres de chemin dans msg.req.params.
Reliez-le à un nœud function doté de deux sorties : la première part vers MQTT, la seconde renvoie directement les erreurs à un nœud http response.
const PREFIX = "connectmq/demo/h7r2pq";
const ALLOWED_DEVICES = ["pump1", "pump2", "valve3"];
const ALLOWED_ACTIONS = ["start", "stop", "setpoint"];
const API_KEY = env.get("BRIDGE_API_KEY"); // à définir dans le flow ou l'environnement
function fail(code, message) {
msg.statusCode = code;
msg.payload = { error: message };
return [null, msg];
}
if (!API_KEY || msg.req.headers["x-api-key"] !== API_KEY) {
return fail(401, "invalid or missing API key");
}
const id = msg.req.params.id;
if (!ALLOWED_DEVICES.includes(id)) return fail(404, "unknown device " + id);
const body = msg.payload;
if (typeof body !== "object" || body === null) return fail(400, "JSON body required");
if (!ALLOWED_ACTIONS.includes(body.action)) return fail(400, "action must be one of " + ALLOWED_ACTIONS.join(", "));
if (body.action === "setpoint" && typeof body.value !== "number") {
return fail(400, "setpoint requires a numeric value");
}
const correlationId = Date.now().toString(36) + Math.random().toString(36).slice(2, 8);
const command = {
action: body.action,
value: body.value,
correlationId,
issuedAt: new Date().toISOString(),
source: "http"
};
msg.deviceId = id;
msg.correlationId = correlationId;
msg.wait = msg.req.query.wait === "1";
msg.topic = `${PREFIX}/${id}/cmd`;
msg.payload = JSON.stringify(command);
msg.qos = 1;
msg.retain = false;
return [msg, null];
Ne construisez jamais le topic MQTT directement à partir d'une saisie utilisateur sans la vérifier contre une liste autorisée. Sinon, une requête pour l'équipement # ou ../../other publierait sur des topics que vous n'aviez jamais l'intention d'exposer.
Étape 2 : publier et répondre
Reliez la première sortie à deux endroits :
- Un nœud mqtt out avec un champ topic vide, afin qu'il utilise
msg.topic, connecté au nœud de configuration de votre broker. - Un nœud function qui décide comment répondre à l'appelant HTTP :
if (msg.wait) {
// Mettre la requête en attente jusqu'à l'acquittement ou l'expiration du minuteur
const pending = flow.get("pending") || {};
pending[msg.correlationId] = msg;
flow.set("pending", pending);
setTimeout(() => {
const p = flow.get("pending") || {};
const waiting = p[msg.correlationId];
if (waiting) {
delete p[msg.correlationId];
flow.set("pending", p);
waiting.statusCode = 504;
waiting.payload = { error: "device did not acknowledge in time", correlationId: msg.correlationId };
node.send(waiting);
}
}, 5000);
return null;
}
msg.statusCode = 202;
msg.payload = { status: "accepted", correlationId: msg.correlationId, topic: msg.topic };
return msg;
Reliez ce nœud function et la sortie d'erreur de l'étape 1 au même nœud http response. Celui-ci utilise msg.statusCode et envoie msg.payload au format JSON.
La réponse asynchrone 202 est le bon choix par défaut. Les équipements MQTT peuvent être en veille, en cours de reconnexion ou occupés, et un client HTTP ne doit pas rester bloqué pour autant. La variante wait=1 est utile pour les outils interactifs, lorsqu'une personne clique sur un bouton et veut savoir si la pompe a réellement démarré.
Étape 3 : traiter les acquittements des équipements
L'équipement publie un acquittement qui reprend l'identifiant de corrélation, par exemple sur connectmq/demo/h7r2pq/pump1/ack :
{ "correlationId": "lrx2k9ab12cd", "result": "ok", "state": "running" }
Abonnez-vous avec un nœud mqtt in à connectmq/demo/h7r2pq/+/ack (sortie : JSON analysé) et associez-le à la requête en attente :
const ack = msg.payload;
const pending = flow.get("pending") || {};
const waiting = pending[ack.correlationId];
if (!waiting) return null; // déjà expirée, ou requête 202
delete pending[ack.correlationId];
flow.set("pending", pending);
waiting.statusCode = ack.result === "ok" ? 200 : 409;
waiting.payload = { status: ack.result, state: ack.state, correlationId: ack.correlationId };
return waiting;
Reliez-le au même nœud http response. Comme le message en attente transporte toujours l'objet msg.res d'origine, la réponse revient au bon appelant HTTP. Les requêtes en attente vivent en mémoire, ce qui ne pose pas de problème : les connexions HTTP auxquelles elles appartiennent disparaissent elles aussi au redémarrage de Node-RED.
Si vous utilisez MQTT 5, vous pouvez définir msg.responseTopic et msg.correlationData sur la commande sortante au lieu d'intégrer l'identifiant dans le JSON. Le nœud mqtt out les transmet sous forme de propriétés MQTT 5, et les équipements qui les prennent en charge peuvent répondre sur le topic demandé. Le guide des fonctionnalités de MQTT 5 détaille le modèle requête-réponse.
Étape 4 : un endpoint de lecture pour l'état des équipements
Les équipements doivent publier leur état sous forme de retained messages sur connectmq/demo/h7r2pq/<id>/state. Abonnez-vous à connectmq/demo/h7r2pq/+/state et stockez chaque état dans le contexte du flow :
const id = msg.topic.split("/")[3];
const states = flow.get("states") || {};
states[id] = { state: msg.payload, receivedAt: new Date().toISOString() };
flow.set("states", states);
return null;
Ajoutez ensuite un nœud http in pour GET /api/devices/:id/state :
const states = flow.get("states") || {};
const s = states[msg.req.params.id];
if (!s) {
msg.statusCode = 404;
msg.payload = { error: "no state received for this device" };
} else {
msg.statusCode = 200;
msg.payload = s;
}
return msg;
Comme les messages d'état sont retenus, Node-RED reçoit la dernière valeur de chaque équipement dès sa reconnexion : l'endpoint de lecture est donc utilisable immédiatement après un redémarrage.
Tester avec curl
Définissez la clé d'API comme variable d'environnement pour Node-RED (BRIDGE_API_KEY=change-me), déployez, puis appelez les endpoints sur le port par défaut 1880 :
# Envoyer sans attendre
curl -i -X POST http://localhost:1880/api/devices/pump1/command \
-H "Content-Type: application/json" -H "X-API-Key: change-me" \
-d '{"action":"start"}'
# Attendre l'acquittement (renvoie 504 après 5 s si aucun équipement ne répond)
curl -i -X POST "http://localhost:1880/api/devices/pump1/command?wait=1" \
-H "Content-Type: application/json" -H "X-API-Key: change-me" \
-d '{"action":"setpoint","value":42.5}'
# Simuler l'acquittement de l'équipement (copiez le correlationId depuis le topic cmd)
mosquitto_sub -h broker.connectmq.com -p 1883 -i dev-h7r2pq -v -t "connectmq/demo/h7r2pq/pump1/cmd"
mosquitto_pub -h broker.connectmq.com -p 1883 -i ack-h7r2pq -t "connectmq/demo/h7r2pq/pump1/ack" \
-m '{"correlationId":"PASTE_ID","result":"ok","state":"running"}'
Sécuriser une passerelle Node-RED HTTP vers MQTT
Un endpoint HTTP qui actionne des pompes constitue une surface d'attaque. Parcourez cette liste avant de l'exposer au-delà de localhost :
- Authentifiez chaque requête. La vérification de clé d'API ci-dessus est un minimum. Pour une authentification basique sur tous les endpoints HTTP-in, définissez
httpNodeAuthdanssettings.jsavec un hachage bcrypt généré parnode-red admin hash-pw. - Terminez le TLS devant Node-RED avec un reverse proxy comme Nginx ou Caddy. N'exposez pas directement le port 1880 sur Internet.
- Protégez l'éditeur avec
adminAuth, ou servez l'éditeur sur un chemin ou un port distinct, uniquement interne. - Validez les entrées à l'aide de listes autorisées pour les équipements et les actions, et vérifiez le type des valeurs.
- Limitez le débit au niveau du proxy, en gardant à l'esprit que le broker applique lui aussi des limites. Le broker public autorise 10 messages par seconde et par client, et les offres de production ont leurs propres quotas.
- Utilisez des identifiants de broker privés via TLS sur le port 8883 pour les nœuds mqtt.
Notre article sur la sécurisation de Node-RED traite en détail de l'éditeur et des paramètres.
Passer à un broker de production
Remplacez la configuration du broker de démo par un broker ConnectMQ privé : port 8883, TLS activé, et les valeurs YOUR_USERNAME, YOUR_PASSWORD et YOUR_CLIENT_ID générées dans le portail. ConnectMQ ne propose ni webhooks ni API de publication HTTP propre, et c'est précisément pour cela que ce modèle existe : Node-RED est la couche exposée en HTTP, et le broker reste concentré sur MQTT. Avec un broker MQTT managé, le seul serveur dont vous vous occupez est l'instance Node-RED.
Faites tourner votre passerelle Node-RED sur ConnectMQ
Créez gratuitement un broker sur app.connectmq.com avec l'offre Serverless, sans carte bancaire, et connectez-y votre passerelle Node-RED via TLS. Vous pouvez suivre les commandes et les acquittements dans l'explorateur de topics en direct du portail pendant que vous testez vos endpoints avec curl. Pour les bases des nœuds MQTT, consultez le tutoriel Node-RED MQTT et, pour le sens inverse, l'envoi d'alertes à des personnes, le guide du bot Telegram avec Node-RED.
Questions fréquentes
Node-RED peut-il recevoir des webhooks et les publier sur MQTT ?
Oui. Un nœud http in accepte le webhook, un nœud function le valide et construit un topic, et un nœud mqtt out le publie. Renvoyez rapidement une réponse 2xx pour que l'émetteur du webhook ne réessaie pas.
L'endpoint HTTP doit-il attendre la réponse de l'équipement ?
Renvoyez 202 Accepted par défaut et laissez les clients consulter l'état séparément. Attendre un acquittement est utile en usage interactif, mais imposez toujours un délai d'expiration court.
Quelle QoS utiliser pour les commandes venant de HTTP ?
La QoS 1 est un bon choix par défaut pour les commandes, combinée à des actions idempotentes comme start et stop plutôt qu'une bascule (toggle), afin qu'une livraison en double ne cause aucun dommage.
ConnectMQ fournit-il une API REST de publication ?
Non. ConnectMQ est un broker MQTT doté d'endpoints MQTT, TLS et WebSocket. Utilisez Node-RED ou un petit service de votre cru pour traduire les requêtes HTTP en publications MQTT.



