Skip to main content
The Realtime service provides WebSocket and Server-Sent Events (SSE) connections for live data updates in your SnackBase collections.

Overview

Subscribe to collection changes and receive instant updates when records are created, updated, or deleted:
import { SnackBaseClient } from "@snackbase/sdk";

const client = new SnackBaseClient({
  baseUrl: "https://api.example.com",
});

// Connect to the realtime service
await client.realtime.connect();

// Subscribe to a collection
await client.realtime.subscribe("posts", ["create", "update", "delete"]);

// Listen for events
client.realtime.on("posts.create", (data) => {
  console.log("New post created:", data);
});

client.realtime.on("posts.update", (data) => {
  console.log("Post updated:", data);
});

client.realtime.on("posts.delete", (data) => {
  console.log("Post deleted:", data);
});

Connection Methods

SnackBase realtime supports two connection methods:
MethodDescriptionBrowser SupportUse Case
WebSocketFull-duplex, low latencyModern browsersInteractive apps
SSESimple, one-way server to clientAll browsersSimple updates, fallback
The SDK automatically uses WebSocket when available and falls back to SSE.

Getting Started

1. Connect

Connect to the realtime service:
await client.realtime.connect();
The SDK requires an authenticated user for realtime connections. Ensure the user is logged in before connecting.

2. Subscribe to Collections

Subscribe to events on a collection:
await client.realtime.subscribe("posts", ["create", "update", "delete"]);

3. Listen for Events

Listen for specific events:
client.realtime.on("posts.create", (data) => {
  console.log("New post:", data);
});

4. Disconnect

Disconnect when done:
client.realtime.disconnect();

Connection States

Monitor the connection state:
// Get current state
const state = client.realtime.getState(); // "disconnected" | "connecting" | "connected" | "error"

// Listen for state changes
client.realtime.on("connected", () => {
  console.log("Connected to realtime");
});

client.realtime.on("disconnected", () => {
  console.log("Disconnected from realtime");
});

client.realtime.on("error", (error) => {
  console.error("Realtime error:", error);
});

Subscription Events

Subscribe to specific operations:
// All operations
await client.realtime.subscribe("posts");

// Specific operations
await client.realtime.subscribe("posts", ["create"]);
await client.realtime.subscribe("posts", ["create", "update"]);
await client.realtime.subscribe("posts", ["create", "update", "delete"]);

Event Data Structure

Events include the complete record data:
client.realtime.on("posts.create", (data) => {
  console.log(data.id);
  console.log(data.title);
  console.log(data.content);
  console.log(data.createdAt);
  // ... all record fields
});

Multiple Subscriptions

Subscribe to multiple collections:
await client.realtime.subscribe("posts");
await client.realtime.subscribe("comments");
await client.realtime.subscribe("likes");

client.realtime.on("posts.create", (data) => {
  console.log("New post:", data);
});

client.realtime.on("comments.create", (data) => {
  console.log("New comment:", data);
});

client.realtime.on("likes.create", (data) => {
  console.log("New like:", data);
});

Wildcard Events

Use wildcards to listen to all events:
// All events on all collections
client.realtime.on("*", (data) => {
  console.log("Any event:", data);
});

// All events on a specific collection
client.realtime.on("posts.*", (data) => {
  console.log("Any posts event:", data);
});

Unsubscribing

Unsubscribe from a collection:
await client.realtime.unsubscribe("posts");
Remove event listeners:
// The on() method returns an unsubscribe function
const unsubscribe = client.realtime.on("posts.create", (data) => {
  console.log("New post:", data);
});

// Stop listening
unsubscribe();

Complete Example

import { SnackBaseClient } from "@snackbase/sdk";

const client = new SnackBaseClient({
  baseUrl: "https://api.example.com",
});

async function setupRealtime() {
  // Authenticate first
  await client.auth.loginWithPassword({
    email: "[email protected]",
    password: "password",
  });

  // Connect to realtime
  await client.realtime.connect();

  // Subscribe to posts
  await client.realtime.subscribe("posts", ["create", "update", "delete"]);

  // Listen for events
  client.realtime.on("posts.create", (post) => {
    console.log("New post created:", post.title);
    // Update UI
  });

  client.realtime.on("posts.update", (post) => {
    console.log("Post updated:", post.title);
    // Update UI
  });

  client.realtime.on("posts.delete", (post) => {
    console.log("Post deleted:", post.id);
    // Remove from UI
  });

  // Handle connection events
  client.realtime.on("connected", () => {
    console.log("Realtime connected");
  });

  client.realtime.on("disconnected", () => {
    console.log("Realtime disconnected - will reconnect");
  });

  client.realtime.on("error", (error) => {
    console.error("Realtime error:", error);
  });
}

React Integration

Use realtime with React:
import { useEffect, useState } from "react";
import { useSnackBase } from "@snackbase/sdk/react";

function PostList() {
  const client = useSnackBase();
  const [posts, setPosts] = useState<Post[]>([]);

  useEffect(() => {
    let unsubscribe: (() => void) | undefined;

    async function setup() {
      // Connect
      await client.realtime.connect();

      // Subscribe
      await client.realtime.subscribe("posts");

      // Listen for new posts
      unsubscribe = client.realtime.on("posts.create", (post) => {
        setPosts((prev) => [...prev, post]);
      });

      // Listen for updates
      client.realtime.on("posts.update", (post) => {
        setPosts((prev) =>
          prev.map((p) => (p.id === post.id ? post : p))
        );
      });

      // Listen for deletions
      client.realtime.on("posts.delete", (post) => {
        setPosts((prev) => prev.filter((p) => p.id !== post.id));
      });
    }

    setup();

    // Cleanup
    return () => {
      unsubscribe?.();
      client.realtime.disconnect();
    };
  }, [client]);

  return (
    <ul>
      {posts.map((post) => (
        <li key={post.id}>{post.title}</li>
      ))}
    </ul>
  );
}

Reconnection

The SDK automatically reconnects on connection loss:
const client = new SnackBaseClient({
  baseUrl: "https://api.example.com",
  maxRealTimeRetries: 10,        // Max reconnection attempts
  realTimeReconnectionDelay: 1000, // Initial delay (ms)
});
Reconnection uses exponential backoff:
AttemptDelay
11 second
22 seconds
34 seconds
48 seconds
Max30 seconds

Authentication and Realtime

Token refresh is handled automatically:
// Initial connection
await client.realtime.connect();

// Token is refreshed automatically
// Connection is re-established with new token

Error Handling

Handle realtime errors:
client.realtime.on("error", (error) => {
  console.error("Realtime error:", error.message);

  // Check error type
  if (error.message.includes("authentication")) {
    console.error("Auth failed - please log in again");
  } else if (error.message.includes("network")) {
    console.error("Network error - will reconnect");
  }
});

client.realtime.on("auth_error", (error) => {
  console.error("Authentication error:", error);
  // Redirect to login
});

Best Practices

1. Subscribe to What You Need

Only subscribe to collections and operations you need:
// Good - specific operations
await client.realtime.subscribe("posts", ["create"]);

// Avoid - all operations if you only need create
await client.realtime.subscribe("posts");

2. Clean Up Subscriptions

Always unsubscribe when done:
useEffect(() => {
  const unsubscribe = client.realtime.on("posts.create", handler);

  return () => {
    unsubscribe();
    client.realtime.disconnect();
  };
}, []);

3. Handle Connection States

Show connection status to users:
const [connectionState, setConnectionState] = useState("disconnected");

client.realtime.on("connected", () => setConnectionState("connected"));
client.realtime.on("connecting", () => setConnectionState("connecting"));
client.realtime.on("disconnected", () => setConnectionState("disconnected"));

return (
  <div>
    <div className="status">
      {connectionState === "connected" && <span className="green">Live</span>}
      {connectionState === "connecting" && <span className="yellow">Connecting...</span>}
      {connectionState === "disconnected" && <span className="red">Disconnected</span>}
    </div>
  </div>
);

Next Steps

  • WebSocket - WebSocket-specific features
  • SSE - Server-Sent Events
  • Events - Event reference