Realtime
client.realtime manages a WebSocket connection with auto-reconnect.
Subscribe to a collection
Section titled “Subscribe to a collection”const unsub = client.realtime.subscribe<Post>("collection:posts", (event) => { console.log(event.event); // "create" | "update" | "delete" console.log(event.collection); // "posts" console.log(event.record); // Post & BunBaseRecord});
// Unsubscribeunsub();The WebSocket connects lazily on the first subscribe call. Authentication is sent automatically using the current access token.
Subscribe to a single record
Section titled “Subscribe to a single record”const unsub = client.realtime.subscribe<Post>( posts.recordChannel("01JKX..."), (event) => { if (event.event === "update") setPost(event.record); if (event.event === "delete") router.push("/"); },);React example
Section titled “React example”import { useEffect, useState } from "react";import type { Post } from "./types";import { client } from "@/lib/bunbase";
function PostList() { const [items, setItems] = useState<Post[]>([]);
useEffect(() => { // Load initial data client.collection<Post>("posts") .list({ sort: "-_created_at", limit: 50 }) .then((r) => setItems(r.items));
// Subscribe to live updates const unsub = client.realtime.subscribe<Post>("collection:posts", (event) => { if (event.event === "create") { setItems((prev) => [event.record, ...prev]); } if (event.event === "update") { setItems((prev) => prev.map((p) => (p._id === event.record._id ? event.record : p)) ); } if (event.event === "delete") { setItems((prev) => prev.filter((p) => p._id !== event.record._id)); } });
return unsub; // cleanup on unmount }, []);
return ( <ul> {items.map((p) => ( <li key={p._id}>{p.title}</li> ))} </ul> );}Connection lifecycle
Section titled “Connection lifecycle”- Connects lazily on first
subscribe - Sends
authmessage with current token automatically - On unexpected disconnect: reconnects with exponential backoff (500ms → 30s max)
- On reconnect: re-subscribes all active channels automatically
Wait for connection
Section titled “Wait for connection”const off = client.realtime.onConnect(() => { console.log("Connected!"); off();});Fires immediately if already connected.
try { await client.realtime.ping(3000); // 3s timeout console.log("Connection is alive");} catch { console.log("Connection dead or timed out");}Useful for detecting stale connections on mobile (e.g. after backgrounding the app).
Query active subscriptions
Section titled “Query active subscriptions”const channels = await client.realtime.getSubscriptions();// ["collection:posts", "record:posts:01JKX..."]Disconnect
Section titled “Disconnect”client.realtime.disconnect();// Closes WebSocket cleanly. No reconnect will be attempted.Presence
Section titled “Presence”Query which users are currently subscribed to a channel on this server worker:
const users = await client.realtime.presence("collection:posts");console.log(users); // ["01JKX...", "01JKY..."]Returns user IDs of authenticated connections subscribed to the channel. In multi-worker deployments, presence reflects only connections on the worker that receives the query.
You can also set a global callback:
client.realtime.onPresence = (channel, users) => { console.log(`${users.length} users on ${channel}`);};Auth events
Section titled “Auth events”When the server revokes or invalidates your session, it pushes an auth event over the WebSocket:
| Event | Meaning |
|---|---|
session_revoked | This specific session was revoked by an admin |
sessions_purged | All sessions for this user were revoked |
account_deleted | The user account was deleted |
password_changed | The user’s password was changed by an admin |
For session_revoked, sessions_purged, and account_deleted: the SDK automatically clears tokens and calls disconnect(). For password_changed: tokens are cleared but the connection stays open.
The correct way to react is client.auth.onAuthChange — it fires for all auth state changes: login, logout, silent refresh, and server-pushed revocation events over WebSocket.
client.auth.onAuthChange((session) => { if (!session) { // Fires immediately when the server pushes a revocation event. router.push("/login"); }});When a realtime connection is active, onAuthChange(null) fires instantly. This replaces the need for startSessionWatch() polling — that remains useful only in non-WebSocket environments (server-side rendering, API-only clients).
API surface
Section titled “API surface”| Method / Property | Description |
|---|---|
subscribe(channel, callback) | Subscribe and return unsubscribe fn |
onConnect(callback) | Fire once when connected |
ping(timeoutMs?) | Send ping, resolve on pong |
getSubscriptions(timeoutMs?) | List active server-side subscriptions |
presence(channel, timeoutMs?) | Query users subscribed to a channel |
onPresence | Callback for presence responses |
disconnect() | Close connection cleanly |