Channels

Subscribe, publish, replay with rewind, and what happens to a publish made while the connection is down.

Getting a channel

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

Local and free — it returns a handle and touches no network. The wire work happens on the first subscribe or publish.

Call it before you connect if you need listeners ready for early messages. The client dispatches an incoming Message frame by looking the channel up in its own map; a frame for a channel it has never been asked about is dropped, because there is nothing to hand it to.

Subscribing

channel.subscribe(message => {
  console.log(message.name, message.data);
});

A message carries channel, an optional name, optional JSON data, the connectionId that published it and a timestamp.

Filtering by name

channel.subscribe('order.created', message => {
  console.log(message.data);
});

This filter is client-side. The server delivers every message on the channel and the SDK discards what does not match — it saves you a branch, not bandwidth.

Both forms can coexist; each subscribe call adds a listener.

Publishing

await channel.publish('order.created', { id: 7 });
await channel.publish({ name: 'order.created', data: { id: 7 } });

The promise resolves when the server Acks the frame, and rejects on a Nack or after realtimeRequestTimeout (10s). Await it — it is the only signal that the message was accepted rather than merely written into a socket.

publish never throws synchronously, even when the connection is not open. A caller who wrote publish(...).catch(...) would not catch a synchronous throw, and the failure would surface as an unhandled error somewhere else in their app.

Publishing while offline

By default a publish made while the connection is down is held and sent on reconnect:

new Blackevin.Realtime({
  key,
  queueMessages: true,
  maxQueuedMessages: 100,
});

The queue is in memory, so a closed tab loses it. This buys you a network blip, a laptop lid, a tunnel — not durability.

When the queue is full the oldest frame is dropped and its promise rejects with queue full: oldest message dropped. A queue that grows without bound during a long outage is a memory leak that ends as a tab crash, which loses strictly more than dropping the oldest would.

close() and a failed connection both reject what is queued rather than holding it forever. A connection that hit its ceiling is the exception — see connection state.

Replaying with rewind

A late subscriber can be handed the last N persisted messages the moment it subscribes:

const channel = realtime.channels.get('chat:lobby', { params: { rewind: 20 } });

channel.subscribe(message => {
  console.log(message.data);
});
  • The server caps rewind at 100.
  • Rewound messages arrive as ordinary Message frames, oldest first, before any live one. Your callback cannot tell them apart, which is the point.
  • They go to that connection only. Nothing is re-published, re-persisted, or forwarded to integrations or cluster peers.
  • The same rewind is re-sent on reconnect, so a restored subscription catches up on what it missed while it was down.

rewind: '20' works too — Ably accepts the string form, so this does.

Wildcards

Channel matching on the server supports * for one segment and > for the rest of the tree. Today the SDK subscribes with the exact channel name, so wildcards reach you through capabilities — where metrics.> grants a whole subtree — rather than through channels.get.

Naming

A channel name is any string. Two conventions are worth following:

  • Separate scopes with :chat:lobby, orders:new. It reads well and it is what the capability examples use.
  • URL-encode it in REST calls. A channel is a path segment, so : becomes %3A. The SDK does this for you; hand-written curl does not.

Tenancy

With auth on, every channel name is scoped to the calling key's account before it reaches a store. Two accounts publishing to chat:lobby are on two different channels and cannot see each other, and there is no way to ask for another account's channel — the scope comes from the credential, never from the request.

On this page