Skip to content

Channels and broadcast

Subscribe connections to named channels and publish from WebSocket or HTTP handlers.

AI generated, pending review Updated 10 days ago · Flare 0.3

Channels let connections in the same broadcast domain exchange messages by name. A connection subscribes to one or more channel strings; a publisher sends raw bytes to every subscriber in that domain.

Done by hand, broadcast is a set of sockets you keep and a loop you write over it, and the bugs live in what that loop forgets: a connection that closed without being removed, so a send throws in the middle of the fan-out; the sender getting its own message back because the loop did not skip it; and no way to announce from an HTTP handler or a timer at all, because the socket set is a local inside the connection that owns it. A channel is that set and that loop kept by the runtime: membership drops on close, publish excludes the sender unless you pass { self: true }, a socket that has begun closing is skipped rather than written, and WebSocketChannels gives the same publish to code outside any connection.

ContextBroadcast domain
Node.js processAll WebSocket connections on the host’s host.ws arc
Durable Object instanceResident and hibernating sockets on that instance
Plain Cloudflare Worker (front door)Not available (each socket is pinned to its accepting request)

host.build() fails when a front-door route can reach WebSocketChannels on a plain Worker, because there is no shared domain to publish into.

Use the channel route option to join channels when the connection opens:

host.ws.route("/chat/:room", {
channel: (scope) => scope.input.params.room,
}).message((ws, scope) => {
ws.send(scope.input.message.raw);
});

Return a single string or an array of strings from the selector. You can also call ws.subscribe("lobby") inside open.

host.ws.route("/chat/:room")
.open((ws, scope) => {
ws.subscribe(scope.input.params.room);
})
.message((ws, scope) => {
ws.publish(scope.input.params.room, scope.input.message.raw);
});

publish(channel, message) sends to the channel’s other subscribers; the sending connection is excluded even when it is subscribed to that channel. Pass { self: true } as the third argument to include the sender. Calling ws.publish(message) without a channel broadcasts to every channel this connection is subscribed to, excluding itself.

Inject WebSocketChannels in an HTTP handler to announce from outside any connection:

import { FlareHost, FlareResponse, WebSocketChannels } from "@flare-ts/core";
import { node } from "@flare-ts/core/node";
const host = new FlareHost(node);
host.http.post(
"/rooms/announce",
{ inject: { channels: WebSocketChannels } },
(_ctx, scope) => {
scope.channels.publish("lobby", "server-announcement");
return new FlareResponse(204);
},
);
const app = host.build();
app.run();

Do not register WebSocketChannels yourself. The runtime adapter seeds it with the correct domain for the execution context.

Cross-context publishes send raw wire bytes. There is no route outgoing schema on a channel publish, so agree on message shape across publishers and subscribers in your app.

Channels work inside a Cloudflare Durable Object the same way, including connections the platform has hibernated. Durable Objects covers the instance-side behavior.