Subscribing
Receive a topic's events in order, choose where to start, handle gaps, and follow many topics at once.
const room = client.topic('chat/lobby');
const sub = room.subscribe((msg) => {
console.log(msg.name, msg.publisher, msg.data);
}, { from: { last: 50 } });
await sub.ready; // the node confirmed the subscriptiontopic.subscribe(handler?, options?) returns a Subscription. The
handler runs for every event, in order, one at a time: if it returns a promise, the next event waits for it.
room.subscribe(async (msg) => {
await saveToDatabase(msg); // the next event is delivered after this finishes
});Where to start
from decides what you receive first on stream topics:
from | You receive |
|---|---|
'latest' (default) | Only new events |
'oldest' | Everything the topic still keeps, then new events |
{ last: 50 } | The last 50 events, then new ones |
{ after: cursor } | Everything after a cursor you saved (a Cursor or an "epoch:seq" string), then new ones |
from only applies the first time. After a reconnect, the SDK resumes right after the last event your handler
finished, so nothing is missed or repeated. With a persistent store that also holds
across page reloads.
On kv and signal topics, you first receive the current values (a snapshot), then changes.
Options
| Option | Default | Meaning |
|---|---|---|
from | 'latest' | Where to start when there is no saved position (see above) |
onGap | 'from-oldest' | What to do when the position you asked for is no longer kept: 'from-oldest', 'from-latest' or 'fail' |
types | all | Only these delivery types, e.g. ['stream'] |
events | all | Only these event names, e.g. ['message'] |
The subscription
| Member | Meaning |
|---|---|
ready | A promise that resolves when the node confirms the subscription, and rejects if it refuses (e.g. Forbidden) |
status | 'pending', 'active', 'resyncing' (reconnecting), 'failed', 'ended' or 'closed' |
cursor | The last event this subscription finished handling |
unsubscribe() | Stops it |
It also emits events; on() returns a function that removes the listener:
sub.on('error', (err) => console.error(err.message)); // failed, or your handler threw
sub.on('gap', (gap) => backfill(gap.requested, gap.oldest)); // events were missed, see below
sub.on('ended', (err) => console.warn('ended:', err.codeName)); // e.g. permissions changed
sub.on('snapshot', (current) => console.log(current.length, 'current values'));
sub.on('status', (status) => console.log(status));You can also read events with for await, which ends when the subscription does:
for await (const msg of client.topic('jobs/new').subscribe()) {
await handle(msg);
}Gaps
A topic keeps a limited history (5 MiB by default, and 24 hours at most). If a client was away longer, the events
right after its position may be gone. The subscription then emits gap:
sub.on('gap', ({ reason, requested, oldest }) => {
// reason: 'expired' (history was trimmed) or 'epoch-lost' (the node restarted)
// Events between `requested` and `oldest` are missing: fetch them from your own database if you need them.
});With onGap: 'from-oldest' (the default) the subscription carries on from the oldest event still kept;
'from-latest' skips to new events; 'fail' ends it.
Subscribing twice
Several subscribe() calls on the same topic share one subscription on the network, so subscribing in several
components is cheap. The first call's options apply. The network subscription ends when the last one unsubscribes.
Many topics at once
Pass an array to client.topic() to handle a set of topics together, such as every chat in an inbox:
const inbox = client.topic(['chat/a', 'chat/b', 'chat/c']);
const sub = inbox.subscribe(notify, { types: ['stream'] });
const { ok, failed } = await sub.ready; // settles when every topic has answered
inbox.add('chat/d'); // joins the live subscription
inbox.remove('chat/a'); // leaves itEvents arrive in order within each topic, but not across topics. Pass strict: true to make ready reject if any
topic fails.
Listening to everything
client.on() sees every event the client already receives, from any subscription, with an optional filter. It
never subscribes by itself.
const off = client.on({ topic: 'chat/*', event: 'message', self: false }, (msg) => {
showNotification(msg.topic, msg.publisher);
});| Filter | Matches |
|---|---|
topic | A topic name or pattern (*, trailing >) |
delivery | 'stream', 'kv' or 'signal' |
event | An event name or a list of names |
publisher | An identity or a list of identities |
self | false skips events your own identity published |