O BroadcastChannel no Node.js permite enviar uma mensagem para todas as instâncias conectadas ao mesmo nome de canal dentro do mesmo processo e contexto compatível. A API segue o padrão da Web e pode ser usada pela thread principal e por Worker Threads para distribuir eventos sem manter uma lista manual de destinatários.
O modelo é diferente de MessageChannel. Em vez de duas portas conectadas ponto a ponto, BroadcastChannel funciona como um tópico: cada participante abre o canal pelo mesmo nome, publica mensagens e recebe mensagens enviadas pelos demais participantes.
Neste guia, você aprenderá a criar canais, publicar eventos, receber mensagens, trabalhar com Worker Threads, validar payloads, fechar recursos, versionar protocolos, controlar duplicidade, lidar com ausência de backpressure e decidir quando usar outra solução.
O que é BroadcastChannel?
BroadcastChannel é uma interface de comunicação um-para-muitos. A documentação oficial de BroadcastChannel no Node.js descreve a implementação do runtime. A documentação de BroadcastChannel na MDN explica o padrão compartilhado com navegadores.
Para comunicação direta entre duas portas, consulte MessageChannel no Node.js. Para processamento paralelo, veja Worker Threads no Node.js. O artigo de Event Loop no Node.js ajuda a compreender quando os eventos são processados.
Criando um canal
const channel = new BroadcastChannel('application-events');Participantes que usam exatamente o mesmo nome conseguem trocar mensagens no escopo suportado pela implementação.
Publicando uma mensagem
channel.postMessage({
type: 'cache-invalidated',
key: 'products:list'
});O emissor normalmente não recebe a própria mensagem por meio da mesma instância. Outros canais com o mesmo nome recebem o evento.
Recebendo mensagens
channel.addEventListener('message', event => {
console.log('Mensagem:', event.data);
});Também é possível usar a propriedade onmessage:
channel.onmessage = event => {
processEvent(event.data);
};Clonagem estruturada
Os dados são enviados usando clonagem estruturada. Objetos e arrays são copiados logicamente; o receptor não recebe a mesma referência.
const payload = { count: 1 };
channel.postMessage(payload);
payload.count = 2;O valor recebido representa o estado durante o envio, não mudanças posteriores.
Tipos suportados
Muitos valores podem ser clonados:
- objetos e arrays;
- Map e Set;
- Date e RegExp;
- ArrayBuffer e TypedArray;
- Blob em versões compatíveis;
- erros e outros tipos conforme o runtime.
Funções, sockets e objetos com recursos nativos geralmente não podem ser enviados.
Exemplo com Worker Threads
Na thread principal:
const {
Worker
} = require('node:worker_threads');
const channel = new BroadcastChannel('configuration');
new Worker('./worker.js');
new Worker('./worker.js');
channel.postMessage({
type: 'reload',
version: 8
});No worker:
const channel = new BroadcastChannel('configuration');
channel.onmessage = event => {
if (event.data.type === 'reload') {
reloadConfiguration(event.data.version);
}
};Inicialização e mensagens perdidas
BroadcastChannel não é uma fila persistente. Um participante que abre o canal depois do envio não recebe mensagens antigas.
Para configuração crítica, envie um snapshot inicial por MessagePort ou leia o estado de uma fonte persistente antes de escutar atualizações.
Eventos de cache
Um caso comum é invalidar caches locais de workers:
channel.postMessage({
version: 1,
type: 'invalidate',
resource: 'user',
id: 42
});Cada worker remove a entrada correspondente. O banco ou cache central continua sendo a fonte de verdade.
Não envie dados completos sem necessidade
Broadcast de objetos grandes multiplica custo de clonagem pelo número de receptores. Prefira enviar identificador e versão para que cada participante recarregue apenas quando necessário.
Versionando o protocolo
{
protocol: 1,
type: 'feature-flags-updated',
revision: 12,
occurredAt: '2026-08-15T10:00:00.000Z'
}O campo de versão permite rejeitar ou adaptar mensagens de outra geração do aplicativo.
Validação de payload
function isChannelEvent(value) {
return value
&& Number.isInteger(value.protocol)
&& typeof value.type === 'string';
}
channel.onmessage = event => {
if (!isChannelEvent(event.data)) {
return;
}
handleEvent(event.data);
};Mesmo em comunicação interna, deployments com versões diferentes podem produzir formatos incompatíveis.
Deduplicação
Se um evento pode ser publicado mais de uma vez, inclua ID:
{
eventId: crypto.randomUUID(),
type: 'cache-invalidated',
key: 'home'
}Mantenha uma janela limitada de IDs processados. Não crie um Set que cresce para sempre.
Ordem das mensagens
Não construa regras críticas dependendo de uma ordem global entre vários emissores. Inclua revisão, timestamp ou sequência por origem e ignore eventos antigos.
Concorrência
Dois workers podem receber o mesmo evento e executar ações simultaneamente. Para operações que só podem acontecer uma vez, use lock, transação ou coordenador externo.
Backpressure
A API não oferece o mesmo controle de backpressure de streams. Um emissor rápido pode criar trabalho mais depressa do que os receptores processam.
Use mensagens compactas, coalescimento e limites. Em atualizações frequentes, publique apenas a revisão mais recente.
Coalescimento
let pendingRevision = 0;
let scheduled = false;
function scheduleReload(revision) {
pendingRevision = Math.max(pendingRevision, revision);
if (scheduled) return;
scheduled = true;
setImmediate(async () => {
scheduled = false;
await reloadToRevision(pendingRevision);
});
}Esse padrão evita processar centenas de atualizações intermediárias.
Tratando erros do consumidor
channel.onmessage = event => {
Promise.resolve(handleEvent(event.data))
.catch(error => {
logger.error('broadcast_handler_failed', {
message: error.message
});
});
};Uma rejeição sem captura não deve derrubar a observabilidade do worker.
messageerror
channel.addEventListener('messageerror', event => {
logger.error('broadcast_deserialization_failed');
});Esse evento indica problema ao desserializar dados.
Fechando o canal
channel.close();Feche durante shutdown ou quando o componente deixa de usar o canal.
ref() e unref()
Em versões compatíveis, o canal pode participar da decisão de manter o processo vivo:
channel.unref();Use quando mensagens forem opcionais e não devam impedir o encerramento.
Graceful shutdown
Pare de publicar, conclua os handlers importantes, remova listeners e feche o canal. Consulte Graceful Shutdown no Node.js.
BroadcastChannel não cruza servidores
O recurso não substitui Redis Pub/Sub, Kafka, NATS ou RabbitMQ para comunicação entre máquinas ou processos independentes. Use-o para participantes dentro do escopo suportado pelo runtime.
Cluster
Workers de Cluster são processos separados. Não presuma que um BroadcastChannel de Worker Threads os conectará. Para processos, use IPC ou um broker.
Veja Cluster no Node.js.
Quando usar MessageChannel?
Use MessageChannel quando precisa de comunicação ponto a ponto, respostas correlacionadas, transferência de portas ou canal dedicado.
Quando usar EventEmitter?
Use EventEmitter para eventos síncronos ou assíncronos dentro da mesma thread e do mesmo grafo de objetos.
Quando usar broker externo?
Use broker quando precisa de persistência, confirmação, replay, múltiplas máquinas, grupos de consumidores ou garantia operacional.
Segurança
O nome do canal não é uma credencial. Qualquer componente no mesmo escopo que conheça o nome pode participar.
Não envie segredos. Valide mensagens e reduza capacidades dos workers com o Permission Model no Node.js quando adequado.
Observabilidade
Monitore:
- mensagens enviadas;
- mensagens recebidas;
- tipo do evento;
- tempo de processamento;
- revisões ignoradas;
- erros de validação;
- erros de desserialização;
- canais fechados.
Evite registrar o payload completo.
Testes
Cubra:
- dois participantes;
- vários workers;
- mensagem anterior à inscrição;
- payload inválido;
- evento duplicado;
- revisão antiga;
- handler com erro;
- fechamento;
- alto volume;
- shutdown.
Use o Node Test Runner.
Erros comuns
- Tratar como fila persistente: participantes atrasados perdem eventos.
- Enviar objetos grandes: custo multiplica por receptor.
- Não fechar: recursos permanecem ativos.
- Esperar comunicação entre servidores: o escopo é local.
- Sem versionamento: deployments diferentes quebram.
- Depender de ordem global: emissores concorrentes divergem.
- Sem limite de frequência: receptores ficam sobrecarregados.
Boas práticas
- Use nomes de canal constantes.
- Versione mensagens.
- Valide payloads.
- Inclua revisão ou ID.
- Envie eventos pequenos.
- Coalesça atualizações.
- Não dependa de replay.
- Feche no shutdown.
- Monitore erros.
- Use broker quando o escopo for distribuído.
Conclusão
O BroadcastChannel no Node.js simplifica a distribuição de eventos para múltiplos participantes e Worker Threads que compartilham o mesmo nome de canal.
Ele funciona melhor para notificações locais, como invalidação de cache e atualização de configuração. Como não possui persistência, replay ou backpressure explícito, precisa de mensagens pequenas, protocolo versionado, deduplicação e fechamento correto. Para comunicação distribuída ou garantida, use uma solução externa.



