Documentation

Connecting and Lifecycle

Fetch credentials, connect a channel, observe recovery, and release resources.

Before you start: complete the Quickstart. These snippets belong in its web/app.js; replace the corresponding setup or handler rather than creating a second client and channel.

1. Fetch credentials

The SDK has a built-in production endpoint. Supply a credential provider that calls your application's credential endpoint:

import { createClient } from "@useceleris/client";

const client = createClient({
  credentialProvider: async (request) => {
    const response = await fetch("/api/realtime-credentials", {
      method: "POST",
      headers: { "content-type": "application/json" },
      body: JSON.stringify({
        channelReference: request.channelReference,
        replayLookbackMs: request.replayLookbackMs,
      }),
      signal: request.signal,
    });
    if (!response.ok) throw new Error("Credential request failed.");
    return response.json();
  },
});

const channel = client.channel("demo-room");

The provider receives a channel reference, a reason ("initial" or "reconnect"), and an abort signal. On recovery it also receives disconnectedAt, a Unix-millisecond number, and replayLookbackMs, a duration in milliseconds as a number. These optional fields can be sent directly in JSON; they are not bigint timestamps. The initial request omits them.

Return { payload, signature } exactly as your backend signed it. Forward the abort signal and check the HTTP result. Do not generate, cache, or inspect the signed strings.

Your backend must validate the requested channel and decide replay policy; see Authentication. The provider runs on every connection attempt, including recovery. A provider failure becomes a safe SDK error; its original details are not forwarded.

Constructing a client, channel, or segment does not open a connection. Use baseUrl only for local or self-hosted Celeris, not your application's credential endpoint. See the Client API for options.

2. Observe state, then connect

const events = channel.events();
const stopState = events.onStateChange((state) => {
  console.log("Connection:", state);
});
const stopErrors = events.onError((error) => {
  console.error(error.name, error.message);
});

try {
  await channel.connect();
} catch (error) {
  console.error("Initial connection failed:", error.code);
}

Expected: connecting, then connected. A failed initial attempt rejects the call and sets failed, without also reporting that failure to onError. It is not retried automatically.

Once connected, the channel is automatically a member of default. Use channel.defaultSegment() to access that stream; do not open another connection or call subscribe() just to join it. Register its message listener before connecting, and make sure your credentials grant read/write access to default. The Quickstart instead grants access to chat; see default-segment messaging.

StateWhat your app should do
idleOffer Connect
connectingWait; avoid another connect call
connectedEnable connected operations
reconnectingDisable sending and show connection status
failedExplain the failure and offer an explicit retry
closingWait for cleanup
closedCreate a new channel if the user needs another connection

A Retry button can call channel.connect() when the state is failed. Calling it while connected, connecting, or reconnecting rejects with OperationInProgress.

To cancel a connection attempt, pass an AbortSignal:

// Alternative connect call, not a second simultaneous attempt:
const controller = new AbortController();
const connecting = channel.connect({ signal: controller.signal });
controller.abort();
try {
  await connecting;
} catch (error) {
  console.log(error.code); // Cancelled
}

3. Respond to recovery

Register a recovery listener once when setting up the channel:

const stopRecovery = events.onRecovery((recovery) => {
  console.log("Recovered:", recovery.retryIndex);
  console.log("Gaps possible:", recovery.possibleGaps);
  console.log("Duplicates possible:", recovery.possibleDuplicates);
  // Trigger your application's snapshot reload here.
});

retryIndex is a number equal to the failed reconnect attempts already consumed in the current budget. It is 0 when recovery succeeds without an earlier failed attempt in that budget; it is not a count of socket drops. possibleGaps and possibleDuplicates are always true. For an order dashboard, reload the order from your own API and reconcile live updates using an application-owned version. The SDK does not supply that application-specific endpoint.

Listeners survive reconnects; do not register them again on every connected event. Also handle errors inside any asynchronous work you start in a listener: synchronous listener isolation is not a substitute for catching rejected promises.

See Reconnection and Recovery for triggers, timing, restored interests, replay permissions, and retry exhaustion. The event is not a replay-complete signal.

4. Release resources

Each event registration returns a disposer. Subscription handles have cancel(). The channel owns the socket.

await channel.close();
stopRecovery();
stopErrors();
stopState();

Also release your message and presence listeners and subscription handles when their UI component no longer needs them. Close the channel only if that component owns the shared connection.

close() is idempotent and permanent. It cancels recovery and closes the segment handles too. Do not reuse a closed channel; obtain a new one from the client.

Next: Messaging.

We use Google Analytics cookies to understand how people use Celeris, only if you allow it. See our Cookie Policy.