client.db
client.db is your app’s scalable database. You define tables (columns +
indexes) in the Muhkoo portal; each table is backed
by its own isolated, stateful edge instance and reachable through a sanitized
REST gateway. This namespace wraps that gateway with a typed, ergonomic surface.
Unlike client.kv (per-user, encrypted-at-rest key/value), the
database is app-scoped, queryable, and shared across your app’s users —
authorized by the app key (the appId + environment are resolved server-side
from the key, so a key can only ever reach its own app’s tables).
table(name)
Section titled “table(name)”table<T extends Record<string, unknown>>(name: string): DbTable<T>Returns a handle to a table. The optional type parameter gives the row operations a typed shape:
interface Todo { _id: number; title: string; done: boolean; rank: number }const todos = client.db.table<Todo>("todos");If no column is marked as the primary key in the portal, an auto-incrementing
_id integer is added for you.
Table methods
Section titled “Table methods”insert(values)
Section titled “insert(values)”insert(values: Partial<T>): Promise<{ row: T; id: unknown }>Inserts a row. Returns the created row and its primary-key value. Every field is validated against the table’s schema (type-checked, NOT NULL enforced); unknown columns are rejected.
const { id } = await todos.insert({ title: "Buy milk", done: false, rank: 1 });get(id)
Section titled “get(id)”get(id: string | number): Promise<T | null>Fetches a row by primary key, or null if it doesn’t exist.
query(query?)
Section titled “query(query?)”query(query?: DbQuery): Promise<{ rows: T[]; nextCursor: string | null }>Lists rows with filters, ordering, and keyset pagination. Pass the
nextCursor from a previous result back as cursor to fetch the next page.
const { rows, nextCursor } = await todos.query({ where: [{ column: "done", op: "eq", value: false }], orderBy: { column: "rank", dir: "asc" }, limit: 50,});interface DbQuery { select?: string[]; // columns to return; omit for all where?: DbWhereCondition[]; // AND-combined orderBy?: { column: string; dir?: "asc" | "desc" }; limit?: number; // clamped server-side to 100 cursor?: string; // a previous result's nextCursor}
interface DbWhereCondition { column: string; op: DbFilterOp; value: unknown;}Operators (DbFilterOp): eq, neq, gt, gte, lt, lte, in
(value is an array), like (raw SQL LIKE pattern on a text column),
likeStartsWith / likeContains (the server builds and escapes the pattern for
you). Range operators (gt/gte/lt/lte) apply to integer, real, and
timestamp columns; the like family applies to text columns.
update(id, values)
Section titled “update(id, values)”update(id: string | number, values: Partial<T>): Promise<{ row: T }>Updates a row by primary key. You can’t change the primary key.
delete(id)
Section titled “delete(id)”delete(id: string | number): Promise<number>Deletes a row by primary key. Resolves to the number of rows removed (0 or 1).
Column types
Section titled “Column types”| Type | Stored as | JS value |
|---|---|---|
text | TEXT | string |
integer | INTEGER | number (safe integer) |
real | REAL | number |
boolean | INTEGER (0/1) | boolean |
timestamp | INTEGER (epoch ms) | number or ISO string |
json | TEXT | any JSON-serializable value |
Safety
Section titled “Safety”The REST gateway is the only way to reach the database — your front end never
talks to the database directly. All input is bound as parameters (never string-concatenated),
column and operator names are validated against the table’s schema, and there is
no raw-SQL passthrough. Results are bounded (limit is capped; pagination is
keyset, not offset).
Errors
Section titled “Errors”| Status | Meaning |
|---|---|
400 | Invalid input — unknown column, bad operator/type, or a malformed filter. |
401 | No app key — pass apiKey to the Client. |
402 | Free-tier database quota exceeded (rows read/written or storage). |
404 | Table or row not found. |
409 | Constraint violation (e.g. a unique column), or an unsupported (destructive) schema migration. |
507 | Table at its single-instance capacity (sharding required). |