sequence.
Location updates are not in this log. They are delivered only through im.locations.watch(...). Updates missed while disconnected cannot be recovered with catchUp(...).
What You Can Do
Recovery Flow
In production, use this order:- Read
lastHandledSequencefrom your database. - Immediately open the live
subscribeEvents(...)streams you need. - At the same time, call
im.events.catchUp(lastHandledSequence)to replay missed events. - Feed live events and catch-up events into the same bounded-concurrency queue.
- Deduplicate by
sequence, and advance the saved checkpoint only after all earlier sequences have completed successfully.
catchUp(...) to finish before opening live streams. Historical recovery and live consumption should run together so new events do not fall into a gap while you are catching up.
Concurrent Recovery
During recovery, put catch-up events and live events into one processing queue. The queue may process events concurrently, but checkpoint storage must advance in contiguous sequence order. This example opens message, chat, group, and poll live streams while catch-up is running:catchUp(...) returns TypedEventStream<CatchUpEvent>. If you omit lastHandledSequence, replay starts from the beginning of the log. lastHandledSequence must be a non-negative safe integer; invalid cursors throw ValidationError before the network call.
The catch-up stream ends with catchup.complete:
catchup.complete to overwrite your checkpoint. It only means the historical replay is done. Your live streams are already open and continue receiving new events.
seenSequences prevents processing the same event twice when it appears in both catch-up and live streams. completedSequences ensures the checkpoint only stores the largest sequence for which every earlier event has also completed.
Events have the same shape in catch-up and live streams. Write method return values are still the authoritative result for the write your code just performed; event streams are for observing other changes and asynchronous state.
Stream Consumption
Every server-streaming SDK method returnsTypedEventStream<T>. This includes subscribeEvents(...), catchUp(...), watch(...), and downloadStream(...).
One stream instance has one consumer. Do not consume the same stream with both for await and .on(...). If you need multiple branches, derive them first with .filter(...), .map(...), or .take(...).
for await
for await loop closes the underlying stream.
await using
await using closes the stream when the scope exits. Node.js 18.17 does not support await using natively; on the minimum supported runtime, use try / finally or break out of for await.
.on(...)
stop() cancels the loop and closes the stream. The callback may be async; the SDK waits for the current callback to finish before delivering the next event. If you omit onError, stream errors are thrown asynchronously and cannot be caught by an outer try / catch.
Derived Streams
.filter(...) keeps matching events:
.map(...) transforms events:
.take(n) emits the first n events, then closes the parent stream:
TypedEventStream
Next Steps
- Messages — subscribe to message events
- Chats — subscribe to chat events
- Groups — subscribe to group events
- Polls — subscribe to poll events
- Error Handling — handle stream disconnects, retries, and idempotent writes