Documentation

Connection 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 equivalent setup or handler. Do not create 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. Your provider can send these optional fields 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. This includes recovery attempts. A provider failure becomes a safe SDK error. The SDK does not forward the original failure details.

When you create a client, a channel, or a segment, the SDK 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);
}

The expected states are connecting, then connected. A failed initial attempt rejects the call and sets failed. The SDK does not also report that failure to onError, and it does not retry the attempt 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 you connect, and make sure your credentials grant read/write access to default. The Quickstart instead grants access to chat. See default-segment messaging.

StateWhat your application must do
idleOffer Connect
connectingWait. Do not make another connect call
connectedEnable connected operations
reconnectingShow the connection status. A publish waits in the queue
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. A connect call in the connected, connecting, or reconnecting state 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 you set 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. Then reconcile new messages with an application-owned version. The SDK does not supply that application-specific endpoint.

The SDK makes at most maximumReconnectAttempts failed reconnect attempts in one budget (10 by default, from 1 to 100). Then the channel changes to failed. Each attempt must finish within reconnectTimeoutMs, which is connectTimeoutMs by default.

Listeners survive reconnects. Do not register them again on every connected event. Also handle errors inside any asynchronous work that you start in a listener. Synchronous listener isolation does not catch a rejected promise.

Replay sends at most the last 100 messages of each segment, for at most 2 minutes. default replays when the channel connects. Other segments replay when the connection joins them with subscribe(). A publish starts no replay. If the reconnect credentials have no replay claim, the missed messages are not available, and the recovery event reports the gap. See Reconcile application data.

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. Queued publishes and a pending presence query reject with Cancelled. Do not reuse a closed channel. Obtain a new one from the client.

Next: Messaging.

We use Google cookies for analytics and for advertising measurement, only if you allow them. Until you choose, Google sets no cookies. Refer to our Cookie Policy.