Docs

Channels

How WebSocket channels are named, created, attached, and released, and how to watch their lifecycle.

Channels are the routing layer. Every message is published to a named channel and reaches the connections subscribed to that name, all multiplexed over each client’s single WebSocket. This page covers the channel object itself. Publish and subscribe covers the messages that flow through it.

Getting a channel

const channel = realtime.channels.get('chat:lobby');

get returns the same instance every time you call it with the same name, so any part of your app can call it without coordination. Options are applied on the first call only. For example, a channel with end-to-end encryption:

const secure = realtime.channels.get('secure:room', { cipher: { key } });

The two options are cipher (see End-to-end encryption) and batch (see Publish and subscribe). To change options later, release the channel and get it again.

Naming

A channel name is 1 to 255 characters from A-Z a-z 0-9 : - _, and can’t start with a colon. Dots aren’t allowed. An invalid name is rejected with error 40001.

Use colons for hierarchy, like chat:rooms:42. The hierarchy pairs with capability patterns, so a token holding chat:* can use every channel under the chat: prefix, and with channel rules, which match by prefix too. Channels don’t need to be created ahead of time. Publishing or attaching to a name brings it into existence, and an idle channel costs nothing.

Lifecycle

Subscribing attaches a channel automatically, so most apps never manage the lifecycle. The explicit calls exist for warming a channel before the first message and for walking away from one:

await channel.attach();   // Start receiving before the first subscribe
await channel.detach();   // Stop receiving, keep the local listeners
realtime.channels.release('chat:lobby');  // Detach and forget the instance

detach stops delivery but keeps the instance and its listeners, so a later attach picks up where you left off. release also drops the instance, which is the right call when a UI view is gone for good.

Channel states

Watch the lifecycle with channel.on, and read the current state from channel.state:

channel.on((change) => {
  console.log(change.previous, '->', change.current);
});
State Meaning
initialized Created locally, no attach attempted yet
attaching Attach requested, awaiting server confirmation
attached Messages and presence are flowing
detaching Detach requested, awaiting server confirmation
detached Not receiving until re-attached
suspended Temporarily lost, usually because the connection dropped. The SDK re-attaches on reconnect
failed The attach failed with an error a retry won’t fix, like a missing capability

While a channel is suspended you can keep publishing. With queueMessages on (the default), publishes queue locally and are sent once the connection is back, as described in Connections. A failed channel stays failed until you call attach() again, for example after minting a token with the missing capability.

Discontinuities

After a reconnect, each channel reports whether it resumed cleanly. Listeners receive an update event whose resumed field says whether the gap was replayed:

channel.on('update', (change) => {
  if (!change.resumed) {
    reloadState();  // Messages may have been missed beyond retention
  }
});

resumed: true is the common case and means nothing was missed, because the SDK backfilled the gap from stored history. resumed: false means the connection was down longer than the channel’s retention keeps messages, so rebuild your state from your own source or read history explicitly.

Limits

One connection can hold up to 1,000 attached channels, and your plan caps the concurrent channels across the app, per Limits. Channels have no per-channel rate ceiling. The message rate is per app, so one busy channel may use all of it.