Querying & DML
KalamClient.query(...) executes SQL over HTTP. For Flutter lists, prefer Kalam Sync watch() so the UI reads the local cache.
Parameterized queries
Use $1, $2, … placeholders in SQL and pass params as a Dart list.
params is encoded to JSON and sent to the server, so values should be JSON-compatible (strings, numbers, booleans, null, lists, and maps).
If you need richer server-side typing (timestamps, decimals, JSON columns), keep your SQL explicit (e.g. CAST(...)) and validate the returned values.
Namespaces
KalamDB supports per-tenant namespaces. The SDK exposes this as an optional namespace argument.
This is most useful when your SQL references unqualified tables (e.g. messages instead of alice.messages).
For background, see: SQL Reference: Namespaces.
Reading results
A QueryResponse can contain multiple QueryResult values (one per statement). The most common pattern is to read the first one.
Primary row access (QueryResult.rows)
rows is List<Map<String, KalamCellValue>> (named columns), not List<List<dynamic>>.
Multiple statements
DML: INSERT / UPDATE / DELETE
All SQL goes through query(...):
FILE refs: insert and read
Insert a FILE reference value
If you already have file metadata (for example from a previous read), insert it as JSON:
Read a FILE reference
getDownloadUrl(...) delegates to the shared Rust link-common FileRef
model, the same source used by the TypeScript SDK. The returned path is:
Fetch the URL with the authenticated user’s bearer token. For user tables, SDK
helpers generate an owner-scoped URL: regular users can download only their own
files. Service clients can upload/write through authorized EXECUTE AS USER
flows, but direct cross-user FILE byte downloads require a dba or system
token and an authorized user_id query parameter.
Error handling
When success is false, the server returns an error code/message:
For user-facing apps, prefer mapping error.code to a stable UI message and logging error.details for debugging.
Procedures
Typed procedure clients come from kalam schema gen --languages dart, not from a generic method on KalamClient. Generated KalamFunctions wraps query('CALL ...') the same way TypeScript createKalam(client) does:
JSON parameters and returns are Map<String, Object?>. Named SQL types reuse the generated row classes. REST POST /v1/functions/{namespace}/{procedure} remains a server HTTP path for non-SDK callers.
Next
Generated typed remote queries
After kalam schema gen --languages dart, reuse the generated table codecs and
primary key directly:
InventoryColumns.sku is KalamColumn<String>, so passing a number fails Dart
analysis. Existing .where((c) => c.col('sku').eq(value)) remains available for
dynamic queries. eq(null) and neq(null) produce IS NULL and IS NOT NULL
when the column type permits null. Values use bound parameters.
orderBy on a remote live query sorts the matching rows locally before applying
limit. The limit controls the displayed rows, not retained memory. Use a narrow
filter for large datasets. Scalar string ordering is local and does not promise
server collation equivalence. Use kalam_sync for durable local projections and
offline actions; bind(client) uses remote transport and does not add persistence.
Generated Dart table specs require one declared primary key. Keyless tables still get row codecs, but no sync spec or guessed key. Composite primary keys are rejected by schema compilation to match current server support. Array codecs preserve null arrays and encode/decode each supported scalar, enum, composite, and timestamp element; null array elements are not represented by the current contract.