Client SDK usage
The generated client SDK gives every table a typed set of operations. Initialize it with your API base URL (and auth, if configured).
import { SDK } from "./api/client"; // or "./src/sdk" when pulled — see SDK distribution
const sdk = new SDK({ baseUrl: "http://localhost:3000" });const user = await sdk.users.create({ name: "Bob", email: "bob@example.com" });
const one = await sdk.users.getByPk("user-id"); // primary keys are strings; null if not foundconst { data } = await sdk.users.list(); // list() returns a paginated result
const updated = await sdk.users.update("user-id", { name: "Robert" }); // null if not found
// Upsert (Prisma-style); `where` must target a unique constraintconst upserted = await sdk.users.upsert({ where: { email: "alice@example.com" }, create: { email: "alice@example.com", name: "Alice" }, update: { name: "Alice Updated" },});
await sdk.users.hardDelete("user-id"); // permanent// await sdk.users.softDelete("user-id"); // when softDeleteColumn is configuredgetByPk, update, softDelete, and hardDelete return null when no row matches the id (the
underlying request 404s). Narrow before using the result.
Soft vs hard delete
Section titled “Soft vs hard delete”When a softDeleteColumn is configured:
softDelete(id)sets that column (e.g.deleted_at = NOW());hardDelete(id)permanently deletes (unlessexposeHardDelete: false).- Soft-deleted rows are automatically hidden from
list()andgetByPk(). - Pass
includeSoftDeleted: trueto include them:
const { data } = await sdk.users.list({ includeSoftDeleted: true });Without a softDeleteColumn, only hardDelete exists.
Relationships & eager loading
Section titled “Relationships & eager loading”Use include to load related rows. Return types are inferred automatically — no casts.
const { data: authors } = await sdk.authors.list({ include: { books: true } });// authors[0].books is typed as SelectBooks[]
// Nested includesconst { data } = await sdk.authors.list({ include: { books: { tags: true } },});// data[0].books[0].tags is typed as SelectTags[]Typed include methods
Section titled “Typed include methods”For common patterns the SDK also generates listWith* / getByPkWith* helpers with per-relation
options. How deep these go is controlled by includeMethodsDepth.
const top = await sdk.authors.listWithBooks({ limit: 10, booksInclude: { orderBy: "published_at", order: "desc", limit: 5 },});
const author = await sdk.authors.getByPkWithBooks("author-id", { booksInclude: { orderBy: "published_at", limit: 3 },});Atomic transactions
Section titled “Atomic transactions”Build operations lazily with the $-prefixed methods, then run them in one transaction. All ops
are Zod-validated before BEGIN, and any failure rolls everything back.
const [order, updatedUser] = await sdk.$transaction([ sdk.orders.$create({ user_id: user.id, total: 99 }), sdk.users.$update(user.id, { last_order_at: new Date().toISOString() }),]);// inferred as [SelectOrders, SelectUsers | null]$create, $update, $upsert, $softDelete, and $hardDelete are the lazy builders. The whole
batch posts to POST /v1/transaction. On failure the thrown error carries a .failedAt index and,
for validation failures, an .issues array (the Zod issues).
try { await sdk.$transaction([ sdk.inventory.$update(itemId, { stock: newStock }), sdk.orders.$create({ item_id: itemId, qty: 1 }), ]);} catch (err: any) { console.error(`failed at op ${err.failedAt}:`, err.message, err.issues);}Errors & response shapes
Section titled “Errors & response shapes”- Non-2xx responses throw
Error("<METHOD> <path> failed: <status> <body>"). 404is special-cased to returnnull(sogetByPk/update/softDelete/hardDeleteresolve tonullrather than throwing when the row is absent).list()always returns the paginated shape.
Generated types & enums
Section titled “Generated types & enums”Each table emits Select<Table>, Insert<Table>, and Update<Table> types, plus Zod schemas. Import
paths depend on your outDir:
import type { SelectUsers, InsertUsers, UpdateUsers } from "./api/client/types/users";import type { PaginatedResponse } from "./api/client/types/shared";import { InsertUsersSchema, UpdateUsersSchema } from "./api/client/zod/users";Postgres enum columns become TypeScript string-literal unions (and z.enum(...) in the Zod
schemas) — e.g. a status enum of active|inactive is typed as "active" | "inactive", not a bare
string.
For filtering, sorting, selection, and search see Querying & pagination. For the exact methods, types, and endpoints generated for your schema, see the Generated API example.