# crud _(no submodules)_ ## Purpose `crud` exposes the typed record-level CRUD primitives (`create`, `select`, `update`, `delete`, `selectAll`) used by `SurrealAdapter`. Splitting them into a standalone module keeps `adapter.ts` under the 300-line hard limit and lets advanced consumers reach for the helpers directly when they already hold a `SurrealClientLike` reference. Each helper is a plain async function that takes the client as its first positional argument — it never reads adapter state — which makes the CRUD layer trivially testable with a real fake client and free of hidden lifecycle coupling. ## Features - `surrealCreate` — `INSERT INTO CONTENT $data` (works around the surrealdb.js v2+ create bug) - `surrealSelect` / `surrealUpdate` / `surrealDelete` — record-id-scoped operations using `type::thing($table, $id)` - `surrealSelectAll` — full-table read - Driver errors are normalised through `wrapQueryError` so every failure surfaces as a `SurrealError` with a stable code - `AbortSignal` is honoured at the start of every call via `signal.throwIfAborted()` ## Usage: Basic Example ```ts import { surrealCreate, surrealSelect } from "@suaveplan/surrealdb/adapter/crud"; const created = await surrealCreate(client, "user", { name: "Alice" }); const fetched = await surrealSelect<{ name: string }>(client, "user", "alice"); ``` ## Usage: Advanced Example ```ts import { surrealSelectAll, surrealUpdate, surrealDelete, } from "@suaveplan/surrealdb/adapter/crud"; const all = await surrealSelectAll<{ id: string; archived: boolean }>(client, "doc"); for (const doc of all) { if (!doc.archived) continue; const ac = new AbortController(); setTimeout(() => ac.abort(), 5000); await surrealUpdate(client, "doc", doc.id, { archived: false }, { signal: ac.signal }); await surrealDelete(client, "tombstone", doc.id, { signal: ac.signal }); } ``` ## API Reference ### `surrealCreate(client, table, data, opts?): Promise` Throws `SurrealError(QUERY_FAILED)` when the driver returns zero rows or any other error. ### `surrealSelect(client, table, id, opts?): Promise` Returns `null` when no row matches. Throws `SurrealError(QUERY_FAILED)` on driver errors. ### `surrealUpdate(client, table, id, data, opts?): Promise` Returns `null` when no row matched. Throws `SurrealError(QUERY_FAILED)` on driver errors. ### `surrealDelete(client, table, id, opts?): Promise` Throws `SurrealError(QUERY_FAILED)` on driver errors. ### `surrealSelectAll(client, table, opts?): Promise` Returns the row array (possibly empty). Throws `SurrealError(QUERY_FAILED)` on driver errors. ## Implementation Notes - Each helper accepts a `SurrealClientLike` rather than the full adapter so consumers can reuse them with custom client wrappers, mocks, or in transactional bodies that already hold the client. - `AbortSignal` is consulted only at the entry point; the underlying driver does not currently accept a per-call signal, so once the `await client.query(...)` is in flight cancellation cannot interrupt it. Callers needing harder cancellation should race the call against a timeout external to the helper. - All error paths unify on `wrapQueryError`, except `surrealCreate` which can throw a code-specific `SurrealError(QUERY_FAILED)` when the row array is empty — that case is distinguishable from a thrown driver error because it carries the exact `INSERT INTO
returned no rows` message. - The `flattenRows` helper from `query-helpers` shields callers from the surrealdb.js v2 `[[...]]` result-wrapping detail.