npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@kalamdb/client

v0.5.1-beta.2

Published

KalamDB client library for TypeScript, providing a WebSocket-based interface for interacting with KalamDB servers, supporting live queries, authentication, and real-time updates.

Downloads

208

Readme

@kalamdb/client

Official TypeScript / JavaScript SDK for KalamDB — SQL, realtime rows, and strong tenant isolation in one client.

Status: Beta — the API surface is still evolving.

KalamDB is built for apps where every user or tenant owns a private data space. The same SQL can run for every signed-in customer, while USER tables ensure each query only touches that caller's data. On the frontend, the default realtime API is now live(): you get the current materialized row set, not a stream of low-level diff frames that your UI has to reconcile.

kalamdb.org · Docs · GitHub

@kalamdb/client provides:

  • SQL execution over HTTP
  • materialized live query rows over WebSocket with live() and liveTable()
  • low-level realtime events with liveEvents() when you need raw frames
  • per-user and per-tenant isolation with USER tables
  • FILE upload/download helpers

Runtime targets:

  • Node.js >= 18
  • modern browsers

For React apps, install @kalamdb/react on top of this package. It wraps the same live() controller with KalamProvider, LiveQuery, LiveQueries, useLiveQuery, useLiveQueries, and assistant-friendly derived selection helpers.

Installation

npm i @kalamdb/client

The browser build loads the packaged KalamDB WASM automatically. Most apps do not need to pass a custom wasmUrl.

Why live() First

Most UIs do not want subscription_ack, initial_data_batch, change, and error frames. They want the latest rows.

live() gives you exactly that:

  • the current row set already reconciled for insert, update, and delete
  • one callback shape for initial load and future changes
  • shared behavior with the Rust and Dart clients
  • simpler React, Vue, Svelte, and plain browser code

Use lastRows when you want an initial rewind from the server. Use limit when you want the client to keep the materialized live row set bounded over time.

The knobs apply at different layers:

  • batchSize chunks the initial snapshot from the server
  • lastRows chooses how much history to rewind first
  • limit caps the materialized live row set the client keeps afterward

Use liveEvents() only when you need the raw event protocol.

React users can delegate this row materialization to @kalamdb/react:

import { LiveQuery } from '@kalamdb/react';

<LiveQuery query="SELECT * FROM support.inbox WHERE room = 'main' ORDER BY created_at ASC">
  {({ rows, state }) => (
    <section>{state.loading ? 'Loading' : rows.length}</section>
  )}
</LiveQuery>

If your query does not expose an id column, pass column names through getKey so row reconciliation still stays inside the shared Rust core:

const stop = await client.live(
  "SELECT room_id, message_id, body FROM support.messages WHERE room_id = 'main'",
  (rows) => {
    console.log(rows.length);
  },
  {
    getKey: ['room_id', 'message_id'],
  },
);

Quick Start

Start with a USER table. The SQL stays simple, and KalamDB scopes the data per authenticated user.

CREATE NAMESPACE IF NOT EXISTS support;

CREATE TABLE support.inbox (
  id         BIGINT PRIMARY KEY DEFAULT SNOWFLAKE_ID(),
  room       TEXT NOT NULL DEFAULT 'main',
  role       TEXT NOT NULL,
  body       TEXT NOT NULL,
  created_at TIMESTAMP NOT NULL DEFAULT NOW()
) WITH (TYPE = 'USER');
import { Auth, createClient } from '@kalamdb/client';

const client = createClient({
  url: 'http://localhost:2900',
  authProvider: async () => Auth.basic('alice', 'Secret123!'),
});

function renderInbox(rows) {
  console.log(
    rows.map((row) => ({
      id: row.id.asString(),
      role: row.role.asString(),
      body: row.body.asString(),
    })),
  );
}

const inboxSql = `
  SELECT id, room, role, body, created_at
  FROM support.inbox
  WHERE room = 'main'
`;

const stop = await client.live(
  inboxSql,
  (rows) => {
    // `support.inbox` is a USER table.
    // Every signed-in user can run the same SQL text, but KalamDB only returns
    // that caller's rows. No app-side WHERE user_id = ? filter is required.
    renderInbox(rows);
  },
  {
    limit: 200,
    lastRows: 200,
    onError: (event) => {
      console.error(event.code, event.message);
    },
  },
);

await client.query(
  'INSERT INTO support.inbox (room, role, body) VALUES ($1, $2, $3)',
  ['main', 'user', 'Need help with billing'],
);

// Later:
await stop();
await client.disconnect();

Resume From a Specific SeqId

When you want offline resume or a durable checkpoint, persist the last SeqId you applied and feed it back into from.

import { Auth, SeqId, createClient } from '@kalamdb/client';

const client = createClient({
  url: 'http://localhost:2900',
  authProvider: async () => Auth.jwt(await getFreshToken()),
});

function renderInbox(rows) {
  console.log(rows.length);
}

const inboxSql = `
  SELECT id, room, role, body, created_at
  FROM support.inbox
  WHERE room = 'main'
`;

// Start from a specific known sequence ID.
// Replace '42' with a previously persisted checkpoint string when resuming.
const startFrom = SeqId.from('42');
let latestCheckpoint;

const stop = await client.live(
  inboxSql,
  (rows) => {
    renderInbox(rows);

  },
  {
    limit: 200,
    lastRows: 200,
    ...(startFrom ? { from: startFrom } : {}),
    onCheckpoint: ({ lastSeqId }) => {
      // Persist the last fully applied server sequence so the next session can
      // continue from that exact point.
      latestCheckpoint = lastSeqId.toString();
    },
  },
);

console.log('latest checkpoint', latestCheckpoint);

This pattern is useful for chat threads, activity feeds, audit logs, and any UI that wants fast reconnect without replaying everything from zero.

Preserve Tenant Boundaries in Worker Writes

Background services should keep the same user isolation guarantees as the browser. Direct USER-table and STREAM-table access stays scoped to the authenticated account; executeAsUser() switches subject only when KalamDB authorizes the actor role for the target user's role.

await client.executeAsUser(
  'INSERT INTO support.inbox (room, role, body) VALUES ($1, $2, $3)',
  'alice',
  ['main', 'assistant', 'Your billing issue is being reviewed'],
);

That keeps the write inside Alice's USER-table or STREAM-table partition through an explicit, audited delegation boundary instead of leaking service-side writes into the wrong tenant scope.

Lower-Level Realtime API

If you need raw protocol frames, use liveEvents().

import { ChangeType, MessageType } from '@kalamdb/client';

const stop = await client.liveEvents(
  "SELECT * FROM support.inbox WHERE room = 'main'",
  (event) => {
    // Use this path when you need raw subscription protocol events.
    if (event.type !== MessageType.Change) {
      return;
    }

    if (event.change_type === ChangeType.Insert) {
      console.log('new rows', event.rows);
    }
  },
  { batchSize: 200, lastRows: 200 },
);

Use this API for protocol tooling, debugging, or custom reconciliation. For app UI state, prefer live().

Topics and Workers

Topic workers now live in the separate @kalamdb/consumer package so app-only installs keep the main SDK lean.

Install the worker package only when you need topic consumption or the worker runtime:

npm i @kalamdb/client @kalamdb/consumer

Use @kalamdb/client for app-facing SQL, live rows, subscriptions, auth, and files. Use @kalamdb/consumer for consumeBatch(), ack(), consumer(), and runConsumer().

Files

queryWithFiles() sends multipart uploads directly to KalamDB while keeping the same auth flow as the rest of the SDK.

await client.queryWithFiles(
  'INSERT INTO support.attachments (id, file_data) VALUES ($1, FILE("upload"))',
  { upload: selectedFile },
  ['att_1'],
  (progress) => {
    console.log(progress.file_name, progress.percent);
  },
);

Authentication

authProvider is the canonical way to configure the client.

import { Auth, createClient, type AuthProvider } from '@kalamdb/client';

const authProvider: AuthProvider = async () => {
  const token = await myApp.getOrRefreshJwt();
  return Auth.jwt(token);
};

const client = createClient({
  url: 'http://localhost:2900',
  authProvider,
});

The SDK handles:

  • WASM initialization
  • Basic-auth-to-JWT exchange
  • lazy or eager WebSocket connection
  • reconnect controls and SeqId tracking

Tested Examples

The npm README examples are backed by SDK tests:

  • tests/readme-examples.test.mjs covers live(), resume-from-SeqId, executeAsUser(), and queryWithFiles().
  • tests/single-socket-subscriptions.test.mjs covers shared-socket subscriptions and materialized live rows.

API Pointers

  • query(), queryOne(), queryAll(), queryRows() for SQL reads
  • insert(), update(), delete() for convenience DML
  • live() and liveTable() for materialized realtime rows
  • liveEvents() for low-level subscription frames
  • getSubscriptions() for active subscriptions and typed lastSeqId checkpoints

Full docs: kalamdb.org/docs/sdk/typescript


Browser and Node.js support is powered by a Rust core compiled to WebAssembly (WASM). See DEV.md for build and contribution details.

License

Licensed under the Apache License, Version 2.0 (Apache-2.0). See the packaged LICENSE.txt and NOTICE files.