Reference
Schema and database
For your agentCopy this and give it to the AI you're building with.
Run `cubek skill`, read all of it and follow it exactly (no CLI: download https://cubek.dev/SKILL.md verbatim with curl; don't summarize it); when you change the schema or query the database, follow its "Schema and database" section. Run `cubek check` after every edit until it is clean.
// schema.ts
import { table, t } from "cubek"
export const notes = table("notes", {
id: t.id(), // string ULID, generated
title: t.string(),
body: t.string(),
tags: t.json<string[]>().nullable(), // any JSON value
pinned: t.boolean().default(false),
views: t.number().default(0).min(0),
status: t.enum(["draft", "published"]).default("draft"),
authorId: t.string().nullable(),
createdAt: t.timestamp().default("now"), // set on insert only
}).index("status", "createdAt").search("title", "body")
export const comments = table("comments", { id: t.id(), noteId: t.ref(notes), text: t.string() })
export const sessions = table("sessions", { id: t.id(), tokenHash: t.string().unique(), userId: t.string() })- Also
t.bytes(),.unique({ nocase: true }),.max(n)(strings: length), table.unique("pollId", "voterId"). Broken constraints throw ininsert/update. - schema.ts changes migrate themselves (tables, nullable/defaulted columns,
indexes, unique, search); dropping, retyping or a new check needs
deploy --allow-destructive.
import { db, type Row } from "cubek"
import type { notes } from "../../schema"
export type Note = Row<typeof notes>
const recent = await db.notes.find({
where: { status: "published", createdAt: { gte: since }, or: [{ pinned: true }, { title: { contains: q } }] },
orderBy: [{ pinned: "desc" }, { createdAt: "desc" }, { id: "desc" }], // id breaks ties
limit: 20,
})
const one = await db.notes.findOne({ where: { title: "Hello" } }) // Note | null
const hits = await db.notes.search(q, { where: { status: "published" }, limit: 10 }) // best first
const n = await db.notes.count({ search: q }) // total search hits
const note = await db.notes.insert({ title: "Hi", body: "" }) // returns the row
await db.notes.update(note.id, { pinned: true }) // row | null; pass updatedAt yourself
await db.sql<{ views: number }>`UPDATE notes SET views = views + 1 WHERE id = ${note.id}` // atomic
await db.comments.deleteWhere({ noteId: note.id }) // count
await db.transaction(async (tx) => { // a throw rolls it back
const a = await tx.notes.insert({ title: "A", body: "" })
await tx.comments.insert({ noteId: a.id, text: "first" })
})
const rows = await db.sql<{ status: string; n: number }>`SELECT status, COUNT(*) AS n FROM notes GROUP BY status`- Also
get(id),delete(id)→ boolean,insertMany(rows),updateWhere(where, patch),sum(col, { where }),min/max. - Where:
ne gt gte lt lte in isNull, textcontains startsWith endsWith(literal),like,and/or. - Timestamps are UTC ISO strings (compare with
toISOString()values). db.transactionruns alone (serializable, ≤ 100 ms, nofetch); outside, read-then-write can interleave. No joins: two queries ordb.sql(database.md).