O MQTT.js é o cliente MQTT mais usado em JavaScript. O mesmo pacote roda no Node.js (sobre TCP, TLS ou WebSockets) e no navegador (sobre WebSockets), suporta MQTT 3.1.1 e 5 e, desde a versão 5, traz tipos TypeScript e métodos baseados em promises. Este tutorial cobre a API da v5 de ponta a ponta.
Instalar o MQTT.js
npm install mqttOs exemplos usam ES modules (import) e o sandbox público da HiveMQ. Salve-os como arquivos .mjs ou defina "type": "module" no package.json.
URLs de conexão: qual protocolo em qual lugar
| Esquema da URL | Transporte | Porta típica | Node.js | Navegador |
|---|---|---|---|---|
mqtt:// | TCP simples | 1883 | Sim | Não |
mqtts:// | TLS | 8883 | Sim | Não |
ws:// | WebSocket | 8000, 8083, 80 | Sim | Só em páginas http:// |
wss:// | WebSocket seguro | 8884, 8084, 443 | Sim | Sim |
Portas e caminhos WebSocket mudam de broker para broker — confira as páginas da HiveMQ ou do EMQX, ou leia portas MQTT explicadas.
Assinar e publicar no Node.js
A API clássica baseada em eventos: mqtt.connect() retorna um cliente imediatamente e emite eventos à medida que a conexão avança.
import mqtt from 'mqtt';
const topic = 'testmqtt/js-demo/hello';
const client = mqtt.connect('mqtt://broker.hivemq.com:1883', {
clientId: `js-demo-${Math.random().toString(16).slice(2, 10)}`,
clean: true,
keepalive: 60,
reconnectPeriod: 2000, // ms between reconnect attempts; 0 disables
connectTimeout: 10_000,
});
client.on('connect', () => {
console.log('connected');
client.subscribe('testmqtt/js-demo/#', { qos: 1 }, (err) => {
if (err) return console.error('subscribe failed:', err.message);
client.publish(topic, JSON.stringify({ hello: 'world' }), { qos: 1 });
});
});
client.on('message', (topic, payload, packet) => {
console.log(`${topic} (retain=${packet.retain}): ${payload.toString()}`);
});
client.on('reconnect', () => console.log('reconnecting...'));
client.on('offline', () => console.log('offline'));
client.on('error', (err) => console.error('error:', err.message));payload é um Buffer — chame toString() para texto ou JSON.parse(payload.toString()) para JSON. O evento connect dispara de novo após cada reconexão; com clean: true o broker esquece suas assinaturas, e é por isso que assinar dentro do handler é o padrão seguro. O MQTT.js também refaz as assinaturas ao reconectar por padrão (resubscribe: true).
Async/await com connectAsync
Para scripts, testes e funções serverless, a API de promises do MQTT.js v5 fica muito mais natural de ler:
import mqtt from 'mqtt';
const client = await mqtt.connectAsync('mqtts://broker.hivemq.com:8883', {
protocolVersion: 5,
});
client.on('message', (topic, payload) => {
console.log(topic, payload.toString());
});
try {
const granted = await client.subscribeAsync('testmqtt/js-demo/#', { qos: 1 });
console.log('granted:', granted.map((g) => `${g.topic} QoS ${g.qos}`).join(', '));
await client.publishAsync('testmqtt/js-demo/async', 'sent with publishAsync', {
qos: 1,
retain: false,
});
await new Promise((resolve) => setTimeout(resolve, 2000));
} finally {
await client.endAsync();
}connectAsync() rejeita quando o cliente emite um error antes de conectar — por exemplo, quando o broker rejeita suas credenciais —, então você pode tratar isso com um try/catch normal. Para um host inacessível, ele continua tentando por padrão; passe false como terceiro argumento para falhar rápido. publishAsync() resolve assim que o broker confirma uma mensagem QoS 1 ou 2. Veja níveis de QoS do MQTT para saber o que cada nível garante.
Veja suas mensagens do MQTT.js ao vivo
TLS, credenciais e opções do MQTT 5 no Node.js
Brokers de produção exigem TLS e autenticação. As credenciais vão nas opções de conexão; para um broker com certificado publicamente confiável, mqtts:// é tudo de que você precisa. Para uma CA privada, passe o certificado da CA (e, para mutual TLS, um certificado e uma chave de cliente):
import { readFileSync } from 'node:fs';
import mqtt from 'mqtt';
const client = await mqtt.connectAsync('mqtts://broker.example.com:8883', {
clientId: 'billing-service-01',
username: process.env.MQTT_USERNAME,
password: process.env.MQTT_PASSWORD,
protocolVersion: 5,
clean: false,
properties: { sessionExpiryInterval: 3600 },
// Only for a private CA / mutual TLS:
// ca: readFileSync('ca.crt'),
// cert: readFileSync('client.crt'),
// key: readFileSync('client.key'),
will: {
topic: 'services/billing/status',
payload: Buffer.from('offline'),
qos: 1,
retain: true,
},
});
await client.publishAsync('services/billing/status', 'online', { qos: 1, retain: true });
await client.publishAsync('orders/created', JSON.stringify({ id: 42 }), {
qos: 1,
properties: {
contentType: 'application/json',
messageExpiryInterval: 60,
userProperties: { source: 'billing' },
},
});Nunca defina rejectUnauthorized: false fora de testes locais — isso desliga a verificação de certificados. Mantenha as credenciais em variáveis de ambiente, não no código-fonte. A opção will registra um Last Will que o broker publica se o serviço morrer sem desconectar, e retain: true mantém o status mais recente disponível para novos assinantes (veja mensagens retidas (retained)). Se você realmente precisa das propriedades do MQTT 5 é assunto de MQTT 5 vs 3.1.1.
Encerrando de forma limpa
Um serviço Node.js de longa duração deve desconectar de forma elegante para que as mensagens QoS 1/2 em trânsito terminem e o broker não dispare seu Last Will em um restart normal:
process.on('SIGTERM', async () => {
await client.publishAsync('services/billing/status', 'offline', { qos: 1, retain: true });
await client.endAsync();
process.exit(0);
});MQTT.js no navegador
No navegador, só URLs WebSocket funcionam. Em uma página HTTPS você precisa usar wss://; caso contrário, o navegador bloqueia a conexão como mixed content:
import mqtt from 'mqtt';
const client = mqtt.connect('wss://broker.hivemq.com:8884/mqtt', {
clientId: `web-${crypto.randomUUID().slice(0, 8)}`,
});
client.on('connect', () => client.subscribe('testmqtt/js-demo/#'));
client.on('message', (topic, payload) => {
const li = document.createElement('li');
li.textContent = `${topic}: ${payload.toString()}`;
document.querySelector('#messages').append(li);
});Bundlers modernos como Vite, webpack 5 e o compilador do Next.js escolhem o build para navegador quando você faz import mqtt from 'mqtt'. Se um bundler reclamar de módulos nativos do Node, importe o bundle pré-compilado de mqtt/dist/mqtt.min.js. Sem bundler, carregue esse mesmo arquivo de uma CDN com uma tag <script> e use o objeto global mqtt. O guia de MQTT sobre WebSockets aborda caminhos e portas em detalhes.
Usando MQTT.js no React
O bug mais comum é criar um novo cliente a cada render. Crie o cliente uma única vez dentro do useEffect e sempre o encerre no cleanup — o StrictMode do React monta os efeitos duas vezes em desenvolvimento e, sem cleanup, você ganha conexões e mensagens duplicadas:
import { useEffect, useState } from 'react';
import mqtt from 'mqtt';
export function useMqttMessages(url: string, topic: string) {
const [messages, setMessages] = useState<string[]>([]);
useEffect(() => {
const client = mqtt.connect(url);
client.on('connect', () => client.subscribe(topic));
client.on('message', (_t, payload) => {
setMessages((prev) => [...prev.slice(-99), payload.toString()]);
});
return () => {
client.end();
};
}, [url, topic]);
return messages;
}No Next.js, o MQTT.js usa APIs do navegador, então chame-o apenas a partir de Client Components ('use client') ou de route handlers no servidor usando mqtt:// ou mqtts://. Em aplicações maiores, coloque um único cliente em um context provider e compartilhe-o.
Tratamento de erros
- Sempre registre um listener de
error. No Node.js, um eventoerrornão tratado derruba o processo. - Credenciais erradas continuam tentando. Com a API de eventos, um CONNECT rejeitado emite
errore o MQTT.js continua reconectando. Inspecioneerr.codee chameclient.end()se não fizer sentido tentar de novo. Consulte os códigos com a ferramenta de reason codes MQTT. - Loops infinitos de reconexão com desconexão imediata geralmente indicam um client ID duplicado ou uma ACL rejeitando sua assinatura. Veja o guia de erros de conexão MQTT.
- Buffer offline. Enquanto desconectado, o MQTT.js enfileira as publicações em memória e as envia ao reconectar. Defina
queueQoSZero: falsese dados QoS 0 desatualizados devem ser descartados.
Precisa de um ponto de partida para o seu próprio broker? O gerador de código MQTT gera este código para o seu host, porta, credenciais e tópico — depois teste ao vivo no cliente MQTT online.
Perguntas frequentes
O MQTT.js consegue se conectar à porta 1883 a partir do navegador?
Não. Navegadores não conseguem abrir sockets TCP brutos, então o MQTT.js no navegador precisa usar uma URL ws:// ou wss:// apontando para o listener WebSocket do broker, por exemplo wss://broker.hivemq.com:8884/mqtt. No Node.js você pode usar mqtt://, mqtts://, ws:// ou wss://.
Como uso async/await com o MQTT.js?
O MQTT.js v5 oferece métodos baseados em promises: mqtt.connectAsync() para conectar e client.subscribeAsync(), client.publishAsync() e client.endAsync() no cliente retornado. As mensagens recebidas continuam chegando pelo evento message.
Por que meu app React abre duas conexões MQTT?
O StrictMode do React monta os componentes duas vezes em desenvolvimento, então um efeito que conecta sem cleanup cria dois clientes. Crie o cliente dentro do useEffect e chame client.end() na função de cleanup, ou mantenha um único cliente em um módulo ou context.