O MQTT.js é o cliente MQTT padrão para JavaScript, e usar o MQTT.js com Node.js é a forma mais rápida de criar backends de IoT, pontes e ferramentas de teste. Neste tutorial você vai criar um publisher e um subscriber em Node.js com MQTT.js v5, usar sua API baseada em promises, escolher níveis de QoS, adicionar um Last Will, migrar para TLS e configurar reconexões para um serviço de longa duração. Todos os exemplos rodam no servidor MQTT público (gratuito) broker.connectmq.com, então você só precisa ter o Node.js instalado.
Pré-requisitos
- Node.js 18 ou mais recente (
node --version) - npm, pnpm ou yarn
- Opcional:
mosquitto_subpara acompanhar o tráfego em um segundo terminal
Crie um projeto e instale a biblioteca:
mkdir mqtt-node-demo && cd mqtt-node-demo
npm init -y
npm pkg set type=module
npm install mqtt
Definir "type": "module" permite usar a sintaxe import e await no nível superior. O MQTT.js v5 é escrito em TypeScript e traz suas próprias definições de tipos, então os editores oferecem autocompletar sem pacotes extras.
URLs de Conexão e Portas
O MQTT.js escolhe o transporte a partir do esquema da URL. Nos brokers do ConnectMQ, as portas são:
| Esquema | Porta | Transporte | Uso típico no Node.js |
|---|---|---|---|
mqtt:// |
1883 | TCP simples | Apenas testes locais |
mqtts:// |
8883 | TCP com TLS | Serviços em produção |
ws:// |
8083 | WebSocket | Raramente necessário no Node.js |
wss:// |
8084 (caminho /mqtt) |
WebSocket seguro | Navegadores, proxies restritivos |
No Node.js, prefira mqtts:// na porta 8883. O WebSocket importa para navegadores, que não conseguem abrir sockets TCP brutos; o artigo MQTT sobre WebSocket cobre esse lado.
Seu Primeiro Subscriber com MQTT.js
Salve como 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, // espera 2 s entre tentativas de reconexão
connectTimeout: 10000, // desiste de uma tentativa apó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);
});
Assinar dentro do handler connect é proposital. Com clean: true, o broker esquece as assinaturas quando a conexão cai, então assinar novamente a cada conexão garante que você continue recebendo mensagens após uma reconexão.
Publicando com QoS e um Last Will
Salve como 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);
Execute os dois:
node subscriber.js
# em um segundo terminal
node publisher.js
Algumas coisas estão acontecendo aqui:
connectAsyncé resolvido quando o broker envia o CONNACK, o que é prático em scripts. Em serviços de longa duração, o eventoconnectcontinua útil porque é disparado novamente após cada reconexão.- QoS 1 significa que o broker confirma cada publish, e
publishAsyncsó é resolvido após essa confirmação. Para telemetria de alta frequência em que uma perda ocasional é aceitável, QoS 0 é mais barato. As contrapartidas são explicadas em níveis de QoS do MQTT explicados. - O Last Will é armazenado pelo broker no momento da conexão e publicado se o cliente desaparecer sem uma desconexão limpa, para que outros serviços saibam que o sensor está offline. Retê-lo faz com que novos subscribers vejam o status atual imediatamente.
- Propriedades do MQTT 5, como
messageExpiryInterval, dizem ao broker para descartar a mensagem se ela não puder ser entregue em 60 segundos, o que evita o acúmulo de leituras desatualizadas para subscribers offline.
A área pública do servidor MQTT público pode ser lida por qualquer pessoa, então troque a parte node-a71c do prefixo por uma string aleatória sua e nunca envie dados reais para lá.
Opções de Reconexão que Importam
O MQTT.js reconecta automaticamente. Estas opções definem como ele se comporta:
| Opção | Padrão | Recomendação |
|---|---|---|
reconnectPeriod |
1000 ms | 2000 a 5000 ms para serviços; 0 desativa as reconexões |
connectTimeout |
30000 ms | 10000 ms para detectar mais rápido uma rede inativa |
keepalive |
60 s | 30 a 60 s; valores menores detectam quedas silenciosas mais cedo |
clean |
true |
false com um client ID estável se você precisar de mensagens enfileiradas |
properties.sessionExpiryInterval |
0 | Segundos em que o broker mantém a sessão após a desconexão (MQTT 5) |
queueQoSZero |
true |
Defina como false se mensagens QoS 0 desatualizadas não devem ser enviadas após a reconexão |
Para um serviço que não pode perder mensagens enquanto fica brevemente offline, combine um client ID fixo, clean: false, assinaturas com QoS 1 e um intervalo de expiração de sessão. O broker então enfileira as mensagens para a sessão e as entrega quando o cliente volta.
Estruturando um Serviço Node.js de Longa Duração
Os scripts acima são curtos de propósito. Um serviço que roda por meses precisa de um pouco mais de estrutura, e quase tudo diz respeito ao ciclo de vida, e não ao MQTT em si:
- Crie um cliente por processo e compartilhe-o por meio de um módulo. Abrir uma nova conexão por requisição ou por job desperdiça recursos e logo esbarra nos limites de conexão.
- Trate
connectcomo "pronto". Condicione o trabalho que depende do broker à propriedadeconnecteddo cliente e decida o que fazer quando ela for false: fazer buffer, descartar ou retornar um erro para quem chamou. - Trate os sinais de desligamento. No
SIGTERM(enviado por Docker, Kubernetes e systemd), publique um status final se você usar um, chameendAsync()para que as mensagens QoS 1 em trânsito sejam enviadas e só então encerre. - Registre transições de estado, não cada mensagem. Os eventos
connect,reconnect,offline,closeeerrorsão o que você precisa ao depurar uma indisponibilidade. Logs por mensagem os afogam. - Mantenha os handlers rápidos. O evento
messageroda no event loop do Node. Trabalho pesado, como gravações em banco de dados, deve ser aguardado com cuidado ou enviado para uma fila, para que um sistema downstream lento não atrase todas as outras mensagens.
Esses hábitos importam mais do que o valor de qualquer opção isolada, e se aplicam igualmente a pontes, workers e ferramentas de teste construídas com MQTT.js.
Autenticando em um Broker Privado
Em produção, use as credenciais do seu broker. Com o ConnectMQ, você cria um broker no portal e recebe um nome de usuário, uma senha e um client ID. Carregue-os a partir de variáveis de ambiente:
import mqtt from "mqtt";
const client = mqtt.connect(`mqtts://${process.env.MQTT_HOST}:8883`, {
clientId: process.env.MQTT_CLIENT_ID, // YOUR_CLIENT_ID do portal
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));
O ConnectMQ usa um certificado Let's Encrypt, no qual o Node.js confia de fábrica, então você não precisa passar um arquivo de CA. Nunca defina rejectUnauthorized: false em produção; isso desativa a verificação de certificados e torna o TLS inútil.
Solução de Problemas do MQTT.js no Node.js
- Conecta e desconecta a cada poucos segundos. Outro cliente usa o mesmo client ID. Cada processo em execução precisa do seu próprio ID.
ERR_REQUIRE_ESMouCannot use import statement. Defina"type": "module"nopackage.jsonou useconst mqtt = require("mqtt")em arquivos CommonJS.- Nenhuma mensagem após uma queda de rede. Você assinou apenas uma vez, fora do handler
connect, usandoclean: true. connack timeout. Um firewall está bloqueando a porta 8883. Teste commosquitto_sub -h broker.connectmq.com -p 8883 --capath /etc/ssl/certs -i test-1 -t testou tentewss://na 8084.- Assinatura concedida com QoS 128 ou reason code 0x80 ou superior. O broker rejeitou a assinatura, muitas vezes por um filtro inválido ou pelo limite de assinaturas do servidor MQTT público (10 por cliente).
- A memória cresce enquanto está offline. As mensagens de saída ficam na fila até a reconexão. Limite seu próprio buffer ou use QoS 0 com
queueQoSZero: falsepara telemetria descartável.
Próximos Passos
A partir daqui você pode levar o mesmo cliente para um frontend: React com MQTT constrói um dashboard baseado em hooks sobre o build para navegador do MQTT.js, e MQTT com tipagem segura em TypeScript adiciona topics tipados e payloads validados sobre este código. Para outros runtimes, o guia de bibliotecas cliente MQTT compara as principais opções. A lista completa de opções está no README do MQTT.js.
Comece a Usar o ConnectMQ
Migre seus serviços Node.js do servidor MQTT público, feito para testes, para um broker privado em minutos. Cadastre-se em app.connectmq.com, escolha o plano Serverless gratuito, sem cartão de crédito, crie um broker e coloque as credenciais geradas no seu ambiente. Com um broker MQTT gerenciado, o ConnectMQ opera os endpoints TLS e a infraestrutura do broker enquanto você se concentra no código da sua aplicação.
Perguntas frequentes
O MQTT.js v5 é compatível com código antigo?
Em grande parte, sim. A API de callbacks continua funcionando, e a v5 adiciona métodos baseados em promises, como connectAsync, publishAsync, subscribeAsync e endAsync. As principais mudanças são a reescrita em TypeScript e uma versão mínima mais recente do Node.js.
Devo usar MQTT 3.1.1 ou MQTT 5 com o MQTT.js?
Use protocolVersion: 5 quando o seu broker suportar, como é o caso do ConnectMQ. Você ganha reason codes, expiração de mensagens, expiração de sessão e user properties, enquanto o código de publish e subscribe continua o mesmo.
Quantas conexões um serviço Node.js deve abrir?
Normalmente uma. Um único cliente MQTT.js consegue publicar e assinar muitos topics ao mesmo tempo, e cada conexão extra precisa de seu próprio client ID único e conta para o limite de conexões do seu plano.
Posso usar o MQTT.js com Bun ou Deno?
O MQTT.js é voltado para Node.js e navegadores. Runtimes com boa compatibilidade com Node.js muitas vezes conseguem executá-lo, mas teste com cuidado o TLS e o comportamento de reconexão antes de depender dele.



