Quick Reference

Cheat sheet for Erebus SDK APIs

Quick Reference

Quick reference for common Erebus SDK operations.

Setup & Connection

import { ErebusClient, ErebusClientState } from "@erebus-sh/sdk/client";

// Create client
const client = ErebusClient.createClient({
  client: ErebusClientState.PubSub,
  authBaseUrl: "https://your-auth.com",
  wsBaseUrl: "wss://gateway.erebus.sh" // optional
});

// Connect
client.joinChannel("my-channel");
await client.connect();

// Disconnect
client.close();

Real-Time Messaging

Subscribe

await client.subscribe("topic", (msg) => {
  console.log(msg.payload);
});

Publish

// Without ack
await client.publish("topic", "Hello!");

// With ack
await client.publishWithAck(
  "topic",
  "Hello!",
  (ack) => console.log(ack.success ? "Success" : `Failed: ${ack.error?.message}`),
  3000 // timeout
);

Unsubscribe

client.unsubscribe("topic");

Advanced Subscribe/Unsubscribe

// Subscribe with ACK and options
client.subscribeWithCallback(
  "topic",
  (msg) => console.log(msg.payload),
  (response) => console.log("Subscribed:", response.success),
  10000,
  { streamOldMessages: true }
);

// Unsubscribe with ACK
client.unsubscribeWithCallback(
  "topic",
  (response) => console.log("Unsubscribed:", response.success)
);

Message History

Get Latest Messages

const history = await client.getHistory("topic");
// Returns 50 newest messages by default

Pagination

const page1 = await client.getHistory("topic", {
  limit: 20,
  direction: "backward"
});

const page2 = await client.getHistory("topic", {
  cursor: page1.nextCursor,
  limit: 20,
  direction: "backward"
});

Iterator

const getNext = client.createHistoryIterator("topic", { limit: 50 });
const batch1 = await getNext(); // { items: [...], hasMore: true }
const batch2 = await getNext(); // { items: [...], hasMore: false }
const done = await getNext();   // null

All History

let cursor = null;
const all = [];

do {
  const page = await client.getHistory("topic", {
    cursor,
    limit: 1000
  });
  all.push(...page.items);
  cursor = page.nextCursor;
} while (cursor);

Presence Tracking

Subscribe to Presence

await client.onPresence("topic", (presence) => {
  console.log(presence.clientId, presence.status);
  // status: "online" | "offline"
});

Unsubscribe

client.offPresence("topic", handler);
client.clearPresenceHandlers("topic"); // all handlers

Typed Schema Facade

Setup

import { ErebusPubSubSchemas } from "@erebus-sh/sdk/client";
import { z } from "zod";

const schemas = {
  chat: z.object({
    text: z.string(),
    username: z.string(),
  }),
};

const typed = new ErebusPubSubSchemas(client, schemas);

Publish (Typed)

typed.publish("chat", "room-1", {
  text: "Hello!",
  username: "Alice"
});

Subscribe (Typed)

await typed.subscribe("chat", "room-1", (msg) => {
  console.log(msg.payload.text);     // ✅ string
  console.log(msg.payload.username); // ✅ string
});

History (Typed)

const history = await typed.getHistory("chat", "room-1");
history.items.forEach(msg => {
  console.log(msg.payload.text); // ✅ validated & typed
});

Connection State

// Connection state checks
client.isConnected  // boolean - WebSocket connected
client.isReadable   // boolean - Can receive messages  
client.isWritable   // boolean - Can send messages

// Connection management
await client.connect(10000); // with timeout
client.close(); // graceful shutdown

// Monitor connection state
setInterval(() => {
  if (!client.isConnected) {
    console.log("Connection lost - SDK will reconnect automatically");
  }
}, 5000);

Message Format

interface MessageBody {
  id: string;           // Unique message ID
  topic: string;        // Topic name
  senderId: string;     // Sender's client ID
  seq: string;          // Sequence (ULID)
  sentAt: Date;         // Timestamp
  payload: string;      // Message content
  clientMsgId?: string; // Optional correlation ID
}

Common Patterns

Reconnect Logic

async function connectWithRetry(maxRetries = 3) {
  for (let i = 0; i < maxRetries; i++) {
    try {
      await client.connect();
      return;
    } catch (error) {
      await new Promise(r => setTimeout(r, 1000 * (i + 1)));
    }
  }
  throw new Error("Failed to connect");
}

Catch-Up After Disconnect

const lastSeq = localStorage.getItem("lastSeq");
const missed = await client.getHistory("topic", {
  cursor: lastSeq || undefined,
  direction: "forward"
});

missed.items.forEach(msg => process(msg));

await client.subscribe("topic", (msg) => {
  process(msg);
  localStorage.setItem("lastSeq", msg.seq);
});

Load More Button

let cursor = null;

async function loadMore() {
  const history = await client.getHistory("topic", {
    cursor,
    limit: 20
  });
  
  history.items.forEach(msg => appendToUI(msg));
  cursor = history.nextCursor;
  
  if (!cursor) {
    hideLoadMoreButton();
  }
}

Error Handling

try {
  await client.connect();
} catch (error) {
  if (error.message.includes("Channel must be set")) {
    // Call joinChannel() first
  } else if (error.message.includes("timeout")) {
    // Connection timeout
  }
}

Limits & Constraints

ItemLimit
Message retention3 days
Messages per topic100 (buffered)
History page size1-1000 messages
Default page size50 messages
Connection timeout30 seconds (default)
Publish ack timeout3 seconds (default)