Drizzle ORM & Generator
@kalamdb/orm is the Drizzle package for KalamDB. Use it when you want generated tables, typed queries, or table-level live helpers. It runs on top of @kalamdb/client.
Install it with the client and Drizzle:
npm i @kalamdb/client @kalamdb/orm drizzle-ormDriver quick start
Copy this after you have a generated schema.ts file.
import { Auth, createClient } from '@kalamdb/client';import { kalamDriver } from '@kalamdb/orm';import { messages } from './schema'; const client = createClient({ url: 'http://localhost:2900', authProvider: async () => Auth.basic('admin', 'AdminPass123!'),}); const db = kalamDriver(client);const recent = await db.select().from(messages).limit(20);The driver uses KalamDB SQL over HTTP and normalizes temporal values for Drizzle columns. timestamp(..., { mode: 'date' }), date(..., { mode: 'date' }), and time(...) can read KalamDB’s numeric wire values without app-side parsing.
Table helpers
Use kTable instead of raw pgTable so the schema retains KalamDB table type metadata.
import { bytes, embedding, file, kTable } from '@kalamdb/orm';import { bigint, jsonb, text, timestamp, uuid } from 'drizzle-orm/pg-core'; export const docs = kTable.shared('app.docs', { id: bigint('id', { mode: 'bigint' }).primaryKey(), owner_id: uuid('owner_id').notNull(), title: text('title').notNull(), metadata: jsonb('metadata'), attachment: file('attachment'), raw_bytes: bytes('raw_bytes'), doc_embedding: embedding('doc_embedding', 384), created_at: timestamp('created_at', { mode: 'date' }).notNull(),});Available table helpers:
| Helper | Use for |
|---|---|
kTable.shared(name, columns, options?) | WITH (TYPE = 'SHARED') tables |
kTable.user(name, columns, options?) | per-user isolated tables |
kTable.stream(name, columns, options?) | stream tables |
kTable.system(name, columns, options?) | system metadata tables |
kSystemColumns([...]) | _seq, _deleted, and _commit_seq typed hidden columns |
KalamDB-specific columns:
| Helper | Runtime value |
|---|---|
file(name) | `FileRef |
bytes(name) | `Uint8Array |
embedding(name, dimensions) | `number[] |
Generate schema.ts
Generate a Drizzle schema from a running KalamDB server:
npx kalamdb-orm \ --url http://localhost:2900 \ --user admin \ --password AdminPass123! \ --namespace app \ --include-system-columns \ --out src/db/schema.tsThe generator reads SHOW TABLES, calls DESCRIBE <namespace.table> when column metadata is incomplete, and emits only the imports needed by the generated tables. It preserves primary keys, not-null constraints, system-column options, comments, table config constants such as chat_messagesConfig, and $inferSelect / $inferInsert aliases.
Options:
| Option | Description |
|---|---|
--namespace <name> | Limit output to one or more namespaces. Repeat it or pass comma-separated names. |
--include-system | Include system and dba tables. |
--include-system-columns <mode> | Add typed hidden columns to generated table definitions. Use all, _seq, or _deleted. |
--bigint-mode <mode> | Choose how generated BIGINT columns are emitted. Accepted values are string, bigint, or number; default is string. |
--no-type-aliases | Skip generated $inferSelect and $inferInsert aliases. |
BIGINT defaults to text() because KalamDB transports Int64 values as strings to preserve precision. Choose --bigint-mode bigint when you want Drizzle to coerce values to native bigint, or --bigint-mode number only when values fit safely in JavaScript numbers.
Keep schema.ts fresh during local development
Add a generator script to your app so the command stays short:
{ "scripts": { "schema:gen": "kalamdb-orm --url http://localhost:2900 --user admin --password AdminPass123! --namespace app --out src/db/schema.ts" }}Then let the Kalam CLI rerun that script whenever schema metadata changes:
kalam --watch-schema --namespace app --run "npm run schema:gen" --run-on-startRepeat --namespace for multiple namespaces, add --table app.messages when you only care about one table, or omit filters entirely to watch all non-system schemas. The default poll interval is 5 seconds; use --interval 2s when you want faster regeneration.
Exact KalamDB datatype mapping
| KalamDB type | Generated Drizzle helper | Read behavior |
|---|---|---|
BOOLEAN | boolean() | boolean |
INT | integer() | number |
SMALLINT | smallint() | number |
BIGINT | text() by default | string for Int64 precision |
DOUBLE | doublePrecision() | number |
FLOAT | real() | number |
TEXT | text() | string |
TIMESTAMP | timestamp(..., { mode: 'date' }) | Date when using date mode |
DATETIME | timestamp(..., { mode: 'date' }) | Date when using date mode |
DATE | date(..., { mode: 'date' }) | UTC-date Date |
TIME | time() | HH:mm:ss[.fraction] string |
JSON | jsonb() | JSON value |
BYTES | bytes() | Uint8Array |
EMBEDDING(n) | embedding(name, n) | number[] |
UUID | uuid() | UUID string |
DECIMAL(p,s) | numeric() | exact decimal string |
FILE | file() | `FileRef |
Live table helpers
Use liveTable() for React state, dashboards, and admin UI screens that want the current materialized row set.
import { liveTable } from '@kalamdb/orm';import { messages } from './schema'; const stop = await liveTable(client, messages, (rows) => { setRows(rows);}, { lastRows: 100,}); await stop();liveTable() reuses the same @kalamdb/client connection and normalizes timestamp/date/time fields from the Drizzle table metadata. It accepts the same row-oriented options as client.live(), including lastRows, from, limit, getKey, and onCheckpoint. Use client.liveEvents() directly when you need low-level change events.
UI and example fit
Use this package in UI code or service code that wants typed tables. Worker code should import @kalamdb/consumer only when it needs topic consumption.
Copy This Project Shape
src/ db/ schema.ts # generated by kalamdb-orm client.ts # createClient() + kalamDriver(client) app/ messages.ts # Drizzle queries against generated tables