Connection and reconnects
Connection states, what the SDK does when the network drops, and how to show it in your UI.
A client has one connection for its whole segment, shared by every topic. You connect once; the SDK keeps it alive.
const client = new Folkways.Realtime({ url: 'https://rte.example.com', authUrl: '/api/realtime-token' });
client.connection.on('change', ({ current, previous, reason }) => {
setStatus(current); // 'connecting', 'connected', 'reconnecting', …
});
const session = await client.connect();States
| State | Meaning |
|---|---|
idle | Created, connect() not called yet |
connecting | Opening the first connection |
connected | Live: publishes and events flow |
reconnecting | The connection dropped; the SDK is retrying. Publishes are queued |
offline | Reconnecting has failed for 2 minutes (offlineAfterMs). Retries go on, but new publishes fail at once with NotConnected |
failed | A problem retrying can't fix, such as a revoked key. See connection.error; call connect() again once fixed |
closed | You called close() |
idle → connecting → connected ⇄ reconnecting → offline
│ │
└──────── failed ◄─────────┘ (fatal error; connect() again)What happens when the network drops
You don't need any code for this:
- The SDK reconnects with exponential backoff and jitter, from 0.5 seconds up to 30 seconds between attempts. In a browser, coming back online or returning to the tab retries at once.
- After reconnecting it resubscribes every topic from the last event you handled, and sends queued publishes.
- If WebSockets fail to open twice in a row (blocked by a proxy, for example), it switches to HTTP long-polling, and tries WebSockets again every 5 minutes. The API is the same on both.
- It sends heartbeats and replaces a connection that has gone silent.
The connection fails for good, with state failed, only when retrying can't help: an invalid token twice in a row,
a suspended segment or revoked publishable key, or the same user signed in from too many devices (Replaced).
connect()
client.connect() resolves with the session once connected.
- Temporary failures are retried quietly. If it takes longer than 15 seconds (
connectTimeoutMs),connect()rejects withTimeout, but the client keeps trying in the background. - A fatal error rejects at once with a
RealtimeError.
try {
await client.connect();
} catch (err) {
if (isRealtimeError(err) && err.codeName === 'Timeout') showBanner('Still connecting…');
else throw err;
}To wait for the connection elsewhere, use await client.connection.whenConnected().
Show it in the UI
client.connection is a live value, so it plugs straight into React:
import { useSyncExternalStore } from 'react';
function ConnectionBadge({ client }: { client: Realtime }) {
const { state, transport } = useSyncExternalStore(client.connection.subscribe, client.connection.getSnapshot);
return <span>{state}{transport ? ` via ${transport}` : ''}</span>;
}Errors that aren't tied to one call, such as a failed token refresh, arrive on the connection's error event:
client.connection.on('error', (err) => reportToMonitoring(err));Signed in elsewhere
A segment can limit how many connections one user may have. When a user opens one too many, the oldest connection
is closed with Replaced and its client goes to failed. Show "You're signed in elsewhere" and reconnect only when
the user asks, otherwise the two devices keep replacing each other.
Closing
await client.close(); // stop for good; queued publishes fail with ClientClosed
await client.close({ clear: true }); // also wipe this user's local data, e.g. on sign-outTuning
The defaults suit most apps. Every timing option is listed in Client options.
new Folkways.Realtime({
url,
authUrl,
reconnect: { initialDelayMs: 1_000, maxDelayMs: 60_000 },
offlineAfterMs: 60_000,
transports: ['websocket'], // never fall back to HTTP
logger: consoleLogger('debug'),
});