Skip to Content

Querying & DML

Raw SQL execution

const res = await client.query('SELECT * FROM app.messages LIMIT 10'); console.log(res.status, res.results?.[0]?.named_rows);

With parameters:

const res = await client.query( 'SELECT * FROM app.messages WHERE conversation_id = $1', ['conv_42'], );

From client.ts, params are sent through queryWithParams(sql, JSON.stringify(params)).

Vector search queries

Vector search uses the same query() and helper APIs as any other SQL workflow.

const matches = await client.queryAll( `SELECT d.id, d.title FROM rag.documents AS d JOIN rag.documents_vectors AS v ON v.id = d.id ORDER BY COSINE_DISTANCE(v.doc_embedding, $1) LIMIT 5`, [JSON.stringify(queryEmbedding)], );

Typical SQL setup:

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;

See Vector Search for the full schema and retrieval pattern.

Row helpers (named_rows)

query() returns a raw QueryResponse. Results are exposed via results?.[0]?.named_rows as plain named-column maps from the server.

For ergonomic access, use:

  • queryOne(sql, params?)RowData | null
  • queryAll(sql, params?)RowData[]
  • queryRows<T>(sql, tableName, params?)KalamRow<T>[]

queryOne() and queryAll() are intentionally not generic. They return RowData with KalamCellValue wrappers. Use queryRows<T>() when you want typed application objects plus FILE-aware row helpers.

const one = await client.queryOne('SELECT id, name FROM app.users WHERE id = $1', ['u1']); console.log(one?.id.asString(), one?.name.asString()); const many = await client.queryAll('SELECT id, active FROM app.users'); for (const row of many) { console.log(row.id.asString(), row.active.asBool()); }

For FILE-aware row wrappers (.file() / .downloadUrl()):

interface MessageRow { id: string; sender: string; content: string; } const rows = await client.queryRows<MessageRow>( 'SELECT id, sender, content, avatar FROM app.messages', 'app.messages', ); console.log(rows[0]?.data.sender);

The helper methods above wrap each value as KalamCellValue (.asString(), .asInt(), .asFile(), etc.). See Cell Values for the full API.

Execute as another user

await client.executeAsUser( 'SELECT * FROM app.messages LIMIT 1', 'alice', );

Source behavior:

  • wraps SQL as EXECUTE AS USER 'username' ( <statement> )
  • strips trailing semicolons in inner SQL
  • escapes ' in username
  • requires one non-empty statement and a non-empty username

Convenience DML methods

await client.insert('app.messages', { id: 1, sender: 'alice', content: 'hello', }); await client.update('app.messages', 1, { content: 'updated' }); await client.delete('app.messages', 1);

Important caveat

update() builds SQL string values directly (with quote escaping for strings). For complex/untrusted inputs, prefer query() with parameter placeholders where possible.

Type shape notes from generated declarations

Generated SDK types mark some fields optional:

interface QueryResponse { status: 'success' | 'error'; results?: QueryResult[]; took?: number; error?: ErrorDetail; } interface QueryResult { schema?: SchemaField[]; rows?: unknown[][]; named_rows?: Record<string, unknown>[]; row_count: number; message?: string; }

Handle missing results/schema defensively in generic tooling; prefer named_rows when present.

Last updated on