This example demonstrates how to create a Node.js "forwarder" that listens to an MQTT broker, expects JSON payloads on a topic, and forwards them via HTTP(S) to a PHP backend endpoint. The solution consists of three parts: environment configuration, Node.js dependencies, and the main forwarder script.
#Requirements
Familiarity with Node.js and MQTT.
#What This Example Does — A Quick Overview
This example shows a small Node.js "forwarder" that listens to an MQTT broker, expects JSON payloads on a topic, and forwards them via HTTP(S) to a PHP backend endpoint. The repository files demonstrate the configuration (.env-style snippet), the Node.js runtime dependencies (package.json), and the main script that connects to MQTT, parses incoming messages as JSON, and posts them to the configured web API using Axios.
#The Configuration (.env)
1# if env is local it will allow self-signed certificates
2ENV=local
3
4# domain or ip address
5WEBSITE=192.168.1.102
6
7# port like 80 or 443 or something else
8PORT=8003
9
10# protocol either "http" or "https"
11PROTOCOL=http
The .env snippet contains four environment variables:
- ENV: set to "local" in the example. The script uses this to allow self-signed TLS certificates during local testing.
- WEBSITE: the domain or IP address of your PHP backend.
- PORT: the port the backend listens on.
- PROTOCOL: either "http" or "https".
These variables are combined in the script to construct the destination URL for the forwarded HTTP POST. Using env variables keeps credentials and environment-specific settings out of source code and makes the forwarder easy to deploy across dev/staging/production by changing the environment only.
#The Package Manifest (package.json)
1{
2 "name": "mqttforwarder",
3 "version": "0.1.0",
4 "description": "mqtt message forwarder",
5 "main": "forwarder.js",
6 "repository": {
7 "type": "git",
8 "url": "https://gitlab.com/example/example"
9 },
10 "scripts": {
11 "test": "echo \"Error: no test specified\" && exit 1"
12 },
13 "keywords": [],
14 "author": "",
15 "license": "ISC",
16 "dependencies": {
17 "axios": "^1.7.2",
18 "dotenv": "^16.0.1",
19 "mqtt": "^4.1.0"
20 }
21}
The package.json shows the forwarder's dependencies:
- mqtt: the MQTT client used to connect to and subscribe to topics on the broker.
- axios: the HTTP client used to POST parsed message payloads to the PHP endpoint.
- dotenv: loads the
.envfile intoprocess.envfor local development.
This file allows easy installation with npm install and documents the runtime requirements.
#The Node.js Forwarder Script — What Happens Step by Step
1require("dotenv").config(); // in order to access env variables
2const mqtt = require("mqtt"); // for connecting and subscribing to an MQTT broker
3const axios = require("axios"); // for making ajax requests
4
5// connect to Broker using a url and port
6const client = mqtt.connect("mqtt://example.com:1883");
7
8client.on("connect", () => {
9 // subscribe to a topic
10 // # just means listen to all subtopics too
11 // qos - is the MQTT protocol quality of service option
12 // we don't need here that's why it is 0.
13 client.subscribe("topic/#", { qos: 0 });
14});
15
16client.on("message", function (topic, message) {
17 console.log("this message :", topic + "/" + message.toString());
18
19 try {
20 let path = "/process-endpoint";
21 let env = process.env.ENV;
22 let messageString = message.toString();
23 let data = JSON.parse(messageString); // expecting valid JSON message
24
25 if (env === "local") {
26 // if your testing locally and your PHP backend
27 // is using a self-signed certificate
28 // this will make sure that data is still send
29 process.env["NODE_TLS_REJECT_UNAUTHORIZED"] = 0;
30 }
31
32 const URL = process.env.PROTOCOL + "://" + process.env.WEBSITE + ':' + process.env.PORT + path;
33
34 axios
35 .post(URL, data, {
36 responseType: "json",
37 transformResponse: (body) => body,
38 headers: {
39 "Content-Type": "application/json",
40 },
41 })
42 .then((response) => {
43 console.log(response?.data);
44 })
45 .catch((error) => {
46 console.log(error?.response?.data);
47 });
48 } catch (error) {
49 console.log(error);
50 }
51});
The forwarder script works in the following steps:
- Environment Setup: dotenv loads environment variables so the script can build the target URL.
- MQTT Connection: The script uses
mqtt.connect("mqtt://example.com:1883")to open an MQTT connection. Replace the broker URL and port with your broker's address. - Topic Subscription: On successful connection, it subscribes to
topic/#. The#wildcard means "all subtopics undertopic/" so the forwarder will receive messages published totopic/foo,topic/bar/baz, etc. - Message Processing: When a message arrives the
client.on("message", (topic, message) => { ... })handler runs:- The code calls
message.toString()and attemptsJSON.parse()to convert the payload into an object. That means the publisher must send valid JSON. A malformed payload will throw and get logged. - If
ENV === "local", the script setsNODE_TLS_REJECT_UNAUTHORIZED = 0to allow self-signed certificates for local testing only. This is convenient for development but must never be used in production. - The target URL is built from
PROTOCOL,WEBSITE,PORT, and a fixed path (/process-endpoint). - Axios posts the parsed data as JSON to the PHP endpoint. The code logs the response data on success or logs the error response data on failure.
- The code calls
#Message Flow and Contract
Publisher → MQTT broker → Node forwarder subscribes to topic → forwarder parses JSON → forwarder POSTs JSON to PHP backend.
The unspoken contract here: messages are expected to be valid JSON and shaped in the way the PHP endpoint expects. You should document the expected JSON schema (keys, types) so both producers and the PHP consumer agree.
#Error Handling and Reliability Notes
- Current example does basic try/catch for parse errors and logs Axios responses/errors. It does not implement retries or persistent queuing.
- If the PHP endpoint is temporarily unavailable, the forwarder logs the error but drops the message. For better reliability consider:
- Retry with exponential backoff for transient HTTP errors.
- Use persistent queueing (e.g., local disk queue, Redis, RabbitMQ) to avoid data loss on restart.
- For high-throughput systems, consider batching messages or using a message queue to decouple ingestion from forwarding.
- MQTT QoS: the script subscribes with QoS 0. If message delivery guarantees are important, increase the QoS (publisher and subscriber must support it) to get at-least-once (QoS 1) or exactly-once (QoS 2) delivery characteristics—though QoS 2 is not commonly used in many brokers.
#Security Considerations
- Never set NODE_TLS_REJECT_UNAUTHORIZED = 0 in production — it disables TLS verification and makes MITM attacks possible.
- Protect the PHP endpoint:
- Require authentication (API key, OAuth token, mutual TLS).
- Validate and sanitize incoming JSON; never trust the payload.
- Rate-limit and log to detect abuse.
- If possible, use TLS (https) between the forwarder and backend and authenticate the forwarder (client certs or an API key).
- Consider authenticating to the MQTT broker as well (username/password, TLS).
#Deployment and Scaling Hints
- Run the forwarder as a managed service (systemd, Docker container, or a process manager like PM2). Ensure logs go to a centralized system for observability.
- For scale, run multiple forwarder instances with distinct client IDs (or share subscription load depending on broker features). Ensure idempotency on the PHP side because multiple deliveries could occur (MQTT QoS >0 + network retries).
- Monitor consumer latency and HTTP response codes. Add health checks and restart policies.
#Small Suggested Improvements
- Add validation for incoming JSON (e.g., use Joi or a lightweight schema validator) before forwarding.
- Add retries with jitter for transient network errors when calling the HTTP API.
- Add structured logging (timestamp, topic, message-id if present) for easier troubleshooting.
- Make the destination path configurable via env instead of hardcoding
/process-endpoint. - Consider publishing an acknowledgement message back to an MQTT topic after successful forwarding if you need end-to-end visibility.