snoozestackdocs

Database

Every project's database lives inside its runtime: a database whose shape comes from your committed schema, whose only reader and writer is a function holding the db capability, and whose state — local, per preview, and live — is isolated per namespace. There is no database server to connect to and no client-facing query API to secure.

Why it lives in the runtime

The queries your functions run are in-process reads against local state — no connection pool, no network hop, no N+1 latency cliff. That is not a toy configuration: you get ordinary SQL and real transactional semantics without the operational surface of a database server, and hosted state is continuously replicated to durable storage rather than living on one disk.

It also keeps the whole backend portable. Your data sits under .snoozestack/ next to your storage and signals — one directory that runs identically on your machine, in a container, and hosted, and one directory to copy when you want your data somewhere else. It is a genuine exit, not an export in someone's bespoke shape.

Reading and writing it#

A function granted the db capability gets one method — a parameterized, single-statement query:

snoozestack/functions/notes/index.ts
export default async function handler(req: Request, capabilities) {
const user = capabilities.auth.user;
if (!user) return new Response("unauthorized", { status: 401 });
const { rows, columns } = capabilities.db.query(
"select id, body, created_at from notes where user_id = ? order by created_at desc",
[user.id],
);
return Response.json({ columns, rows });
}

Writes return { changes, lastInsertRowid } instead of rows. One statement per call is enforced — a second ;-separated statement is an error, not a silent no-op — which is what makes string-building attacks against this surface boring. Always pass values as params, never interpolated into the SQL.

SurfaceAvailability
Portal UIRead-only Database → Schema (tables, columns, indexes) and Database → Data (browse rows) — in both the hosted console and the local dashboard
CLIsnoozestack schema build/push, snoozestack migration generate — the schema is files; see Schema & migrations
SDK / HTTPNo direct database client, by design — apps call a function with functions.invoke()

Where the data lives#

  • Locally: .snoozestack/dev.db, built from your schema on first run and kept in sync as you edit it. snoozestack dev reset throws it away and rebuilds (re-running your committed seed.mjs, if any).
  • Hosted: inside the project's runtime container, on a volume replicated continuously to durable storage — restore and host-failure recovery are platform responsibilities, not yours.
  • Per namespace: live (default) and each named preview hold separate databases, so a preview publish can be exercised against its own data.

Your schema isn't locked in#

The defineTable() vocabulary is deliberately portable: every type has a defined mapping in more than one SQL engine, so the schema describes your data rather than an engine. If your product ever outgrows what the runtime provides, the schema — and the ordinary SQL your functions already write — travels with it.