O MQTT já faz a parte difícil de um app de chat: ele entrega cada mensagem a todos os assinantes em tempo real. Um chat sobre MQTT se resume a três decisões: como as salas viram topics, como a presença funciona e como impedir que alguém poste com o nome de outra pessoa. Este tutorial monta um chat com várias salas funcionando em um único arquivo HTML com MQTT.js, conectado via WebSockets seguros, e depois mostra como testá-lo e reforçá-lo.
Design de topics
| Topic | Payload | QoS | Retida | Finalidade |
|---|---|---|---|---|
testmqtt/chat/<room>/messages/<user> | {"text":"…","ts":…} | 1 | Não | Mensagens do chat |
testmqtt/chat/<room>/presence/<user> | {"status":"online"} ou {"status":"offline"} | 1 | Sim | Quem está na sala |
Duas escolhas nesse layout fazem a maior parte do trabalho:
- O remetente faz parte do topic. Um cliente assina
testmqtt/chat/<room>/messages/+e lê o nome de usuário no último nível do topic. Com uma ACL que só deixa cada usuário escrever com o próprio nome (veja abaixo), o remetente não pode mais ser falsificado. Um campo"user"dentro do JSON poderia. - Presença é uma mensagem retida por usuário. Mensagens retidas são entregues no momento em que um cliente assina, então quem chega vê a lista completa de membros na hora, sem perguntar a ninguém.
As salas são só valores diferentes de <room>, então você tem quantas salas quiser sem nenhuma configuração no broker. As boas práticas de design de topics explicam por que hierarquias estáveis com o identificador no final, como esta, escalam bem.
Presença com mensagens retidas e Last Will
A presença usa dois recursos do MQTT juntos:
- Depois de conectar, o cliente publica
{"status":"online"}retida no próprio topic de presença. - No pacote CONNECT, o mesmo cliente registra um Last Will:
{"status":"offline"}, também retido, no mesmo topic.
Se a conexão terminar sem um DISCONNECT MQTT limpo (aba fechada, notebook suspenso, rede caída), o broker publica o will. Como ele é retido, substitui a mensagem "online" e todos os membros veem o usuário ficar offline. Quando o usuário clica em Leave, o cliente apaga a própria presença de propósito publicando um payload retido vazio, o que remove a mensagem retida.
A página de chat completa
Salve isto como chat.html e abra duas vezes: chat.html?user=alice e chat.html?user=bob. Adicione &room=seutime para escolher uma sala. No endpoint de testes compartilhado, escolha um nome de sala incomum para não conversar com desconhecidos.
@@CHAT@@
Alguns detalhes que merecem atenção:
textContent, nuncainnerHTML. Mensagens de chat são entrada não confiável vinda de outros usuários. Renderizá-las como HTML abre uma brecha de cross-site scripting.- Os nomes de sala e de usuário são higienizados para que um nome com
/,+ou#não escape do seu nível de topic. - Suas próprias mensagens voltam pelo broker. Essa é a sua confirmação de entrega. Se preferir renderizá-las localmente, o MQTT 5 permite assinar com
{ qos: 1, nl: true }(No Local) para que o broker não as devolva. - Payloads malformados são ignorados em vez de derrubar o handler, porque qualquer um com permissão de publicação pode mandar qualquer coisa.
Se a configuração do MQTT.js é nova para você, comece por MQTT no navegador com JavaScript puro, que explica as opções de conexão e o fluxo de depuração.
Teste
Abra duas abas (alice e bob) e passe por estas verificações:
- As mensagens fluem nos dois sentidos. Cada mensagem aparece nas duas abas, com o remetente tirado do topic.
- Quem chega depois vê a presença. Abra uma terceira aba: a lista de membros é preenchida na hora pelas mensagens retidas.
- O will dispara. Feche a aba do bob. A alice deve ver o bob em cinza em poucos segundos.
- Leave limpa. Clique em Leave em uma aba: o usuário some da lista em vez de aparecer como offline.
Para ver o tráfego bruto, assine testmqtt/chat/lobby/# no cliente MQTT online, ou pelo terminal:
mosquitto_sub -h broker.connectmq.com -t 'testmqtt/chat/lobby/#' -v
mosquitto_pub -h broker.connectmq.com -q 1 \
-t testmqtt/chat/lobby/messages/deploy-bot \
-m "{\"text\":\"Build 412 deployed\",\"ts\":$(date +%s)000}"O segundo comando mostra que qualquer serviço (CI, monitoramento, um backend) pode postar em uma sala com um único publish. Para mais formas de exercitar o will, veja testar o Last Will.
Acompanhe o lobby no cliente online
Garanta a identidade com ACLs
Em um endpoint de testes aberto, qualquer pessoa pode publicar em testmqtt/chat/lobby/messages/alice. Em produção, dê a cada usuário credenciais próprias no broker e uma ACL que amarre os topics ao nome de usuário. No Mosquitto, regras pattern substituem %u pelo nome de usuário autenticado:
# Everyone can read every room
pattern read testmqtt/chat/+/#
# ...but may only write under their own name
pattern write testmqtt/chat/+/messages/%u
pattern write testmqtt/chat/+/presence/%uO broker verifica o will com as mesmas regras, então um cliente também não consegue registrar um will no topic de presença de outra pessoa. Depois use os testes de permissão de ACL para confirmar que um publish no topic de outro usuário é realmente rejeitado. No MQTT 3.1.1 esse publish geralmente é descartado em silêncio, então verifique se ele nunca chega em vez de esperar um erro.
O que o MQTT não oferece
| Necessidade | Só MQTT | Solução típica |
|---|---|---|
| Histórico de mensagens | Não (só a última mensagem retida por topic) | Um assinante no backend gravando em um banco de dados, mais um endpoint HTTP de histórico |
| Entrega para quem ficou fora por horas | Sessões persistentes enfileiram mensagens, mas dentro dos limites do broker | Banco de dados mais uma busca "desde a última visita" ao reconectar |
| Confirmação de leitura e indicador de digitação | Não incluído | Topics extras, por exemplo .../typing/<user> com QoS 0 |
| Moderação, exclusão de mensagens | Não | Serviço de backend com permissão de publicação em um topic de sistema |
Sessões persistentes também exigem um broker que guarde o estado da sessão. O endpoint de testes compartilhado não guarda, então teste a recuperação de mensagens na sua própria instância.
Um chat em tempo real sobre MQTT é, acima de tudo, design de topics: salas como níveis de topic, o remetente no topic, presença retida com Last Will e ACLs que mantêm cada usuário na sua faixa. Monte tudo em um arquivo HTML, verifique cada comportamento com duas abas e um assinante observando #, e adicione um banco de dados só quando os usuários precisarem de histórico.
Perguntas frequentes
MQTT é um bom protocolo para chat?
Para entrega ao vivo, sim: publish/subscribe se encaixa naturalmente em salas, as mensagens são pequenas e o Last Will dá presença de graça. Mas o MQTT não guarda histórico do chat, então combine-o com um banco de dados se os usuários precisarem rolar para trás ou se atualizar depois de ficarem offline.
Como mostrar quem está online com MQTT?
Cada cliente publica uma mensagem retida 'online' no próprio topic de presença e registra um Last Will retido 'offline' no mesmo topic. Novos clientes recebem todas as mensagens de presença retidas ao assinar, e o broker publica o will quando um cliente some.
Como impedir que usuários se passem uns pelos outros?
Coloque o nome do remetente no topic, por exemplo chat/<sala>/messages/<usuario>, e use uma ACL do broker que só deixe cada usuário publicar com o próprio nome. Assim quem recebe confia no topic, e não em um campo de nome dentro do payload.


