Skip to Content
Examples

TypeScript SDK Examples

The KalamDB repo currently ships four runnable TypeScript examples. Start with the React one if you want the current app pattern.

They live in the repository under:

  • examples/react-ai-chat
  • examples/simple-typescript
  • examples/chat-with-ai
  • examples/summarizer-agent

Each runnable example has its own setup path and its own test.

1. React AI Chat

Repository path: examples/react-ai-chat

What it demonstrates:

  • @kalamdb/react with KalamProvider and LiveQueries
  • one React screen reading conversations, messages, and typing tokens together
  • file attachments, streamed typing tokens, final assistant inserts, and approval actions
  • browser demo mode and the server-backed topic-agent path

Simple multi-table component:

TSX
import { KalamProvider, LiveQueries } from '@kalamdb/react';import { asc, desc, eq } from 'drizzle-orm';import { getExampleClient } from './client';import { conversations, messages, typingTokens } from './schema.generated'; const client = getExampleClient(); export function ChatShell({ selectedConversationId }: { selectedConversationId: string }) {  const queries = {    conversations: {      table: conversations,      orderBy: (table: typeof conversations) => desc(table.updatedAt),      limit: 50,    },    messages: {      table: messages,      where: (table: typeof messages) => eq(table.conversationId, selectedConversationId),      orderBy: (table: typeof messages) => asc(table.createdAt),      deps: [selectedConversationId],    },    typingTokens: {      table: typingTokens,      where: (table: typeof typingTokens) => eq(table.conversationId, selectedConversationId),      orderBy: (table: typeof typingTokens) => asc(table.createdAt),      deps: [selectedConversationId],    },  };   return (    <KalamProvider client={client}>      <LiveQueries queries={queries} deps={[selectedConversationId]}>        {({ conversations, messages, typingTokens, state }) => (          <ChatWorkspace            conversations={conversations.rows}            messages={messages.rows}            typingTokens={typingTokens.rows}            busy={state.loading}          />        )}      </LiveQueries>    </KalamProvider>  );}

Why it matters:

This is the current React app pattern in the repo. One provider owns the client, one LiveQueries block owns the live datasets, and the UI renders rows directly instead of syncing copies into local state.

Run it:

BASH
cd examples/react-ai-chatnpm installnpm run setupnpm run agentnpm run dev

Test it:

BASH
npm test

npm run setup imports chat-app.sql, clears existing example topics, seeds the default conversation, and writes .env.local. For browser-only demo mode, skip setup and set VITE_KALAMDB_DEMO_MODE=true.

2. Realtime Ops Feed

Repository path: examples/simple-typescript

What it demonstrates:

  • live() for live browser updates
  • query() for writes from the browser
  • browser auth with Auth.basic(...)
  • cross-tab realtime sync from one write

Why it matters:

This is the smallest browser example that still feels real. It avoids wrapper layers, keeps the full flow inside one React component, and shows the recommended pattern: one live query returns the current rows while USER-table isolation keeps each signed-in user inside their own data partition.

Run it:

BASH
cd examples/simple-typescriptnpm installnpm run setupnpm run dev

Test it:

BASH
npm test

The Playwright test opens two tabs, inserts one row, and verifies both tabs update.

3. Chat With AI

Repository path: examples/chat-with-ai

What it demonstrates:

  • generated ORM tables in both the browser and the worker
  • topic fan-out from ALTER TOPIC ... ADD SOURCE ...
  • runConsumer<T>() with a generated row type
  • kalamDriver(client) and executeAsUser(...) for typed writes in the worker

Simple worker snippet:

TS
import { Auth } from '@kalamdb/client';import { createConsumerClient, runConsumer } from '@kalamdb/consumer';import { executeAsUser, kalamDriver } from '@kalamdb/orm';import { drizzle } from 'drizzle-orm/pg-proxy';import {  chat_demo_messages as chatMessages,  type ChatDemoMessages,} from './schema.generated'; const client = createConsumerClient({  url: 'http://127.0.0.1:2900',  authProvider: async () => Auth.basic('admin', 'kalamdb123'),}); const db = drizzle(kalamDriver(client)); await runConsumer<ChatDemoMessages>({  client,  name: 'chat-ai-agent',  topic: 'chat_demo.ai_inbox',  groupId: 'chat-ai-agent',  onChange: async (_ctx, change) => {    const row = change.data;    const user = String(row.sender_username ?? '').trim();    if (row.role !== 'user' || !user) return;     await executeAsUser(      client,      db.insert(chatMessages).values({        room: row.room,        role: 'assistant',        author: 'KalamDB Copilot',        sender_username: user,        content: `AI reply: ${row.content}`,      }),      user,    );  },});

Why it matters:

This example shows the full table → topic → worker → browser loop with the current typed SDK path. The browser writes with generated tables, the worker consumes generated row types, and the reply insert stays typed as well.

Run it:

BASH
cd examples/chat-with-ainpm installnpm run setupnpm run agentnpm run dev

Test it:

BASH
npm test

The Playwright test starts the agent, opens two tabs, sends a message, and waits for the assistant reply in both tabs.

4. Summarizer Agent

Repository path: examples/summarizer-agent

What it demonstrates:

  • a worker-only runConsumer() example
  • row enrichment back into the source table
  • a failure sink table for exhausted retries

Why it matters:

If you do not need a browser at all, this is the cleanest place to start. It shows how little code is required to build a useful background worker around KalamDB topics.

Run it:

BASH
cd examples/summarizer-agentnpm installnpm run setupnpm run start

Test it:

BASH
npm test

The integration test inserts a row and waits until the worker writes the summary back.

5. Vector Search Pattern

Repository reference: backend/tests/scenarios/scenario_14_vector_rag.rs

What it demonstrates:

  • EMBEDDING(n) columns for document and attachment vectors
  • ALTER TABLE ... CREATE INDEX ... USING COSINE
  • nearest-neighbor SQL with ORDER BY COSINE_DISTANCE(...) LIMIT k
  • joining vector hits back to FILE-backed document rows

Why it matters:

This is the current KalamDB pattern for semantic retrieval and RAG. You keep rich document rows in one table, keep embeddings in a keyed companion table, and run vector search with normal SQL. The same flow works with TYPE = 'USER', so each signed-in user only searches their own embeddings.

TypeScript query example:

TS
const rows = await client.queryAll(`  SELECT d.id, d.title, d.body  FROM rag.documents AS d  JOIN rag.documents_vectors AS v ON v.id = d.id  ORDER BY COSINE_DISTANCE(v.doc_embedding, '[1.0,0.0,0.0]')  LIMIT 5`);

SQL setup example:

SQL
CREATE TABLE rag.documents_vectors (  id BIGINT PRIMARY KEY,  doc_embedding EMBEDDING(384)) WITH (TYPE = 'USER'); ALTER TABLE rag.documents_vectors  CREATE INDEX doc_embedding USING COSINE;

Read next:

Last updated on