Quick Start

Define your schema once, pass relations and (for SQLite) migrations, create a client for your runtime, and run your first query. One mental model—portable across adapters.


1) Define a schema with relations

1// db/schema.ts
2import { defineSchema, vibecodeTable, int, text, boolean, datetime, references, col } from '@vibecode-db/client'
3
4export const db = defineSchema({
5  users: vibecodeTable({
6    id: int().primaryKey(),
7    name: text().min(1),
8    email: text().email(),
9  }),
10
11  todos: vibecodeTable({
12    id: text().primaryKey(),
13    title: text(),
14    completed: boolean().default(false),
15    created_at: datetime(),
16    updated_at: datetime(),
17    user_id: references(col.integer('user_id'), () => db.tables.users.id),
18  }),
19})
  • The schema above produces db.zodBundle (Zod validators) and db.relations.
  • Use the explicit references(col.integer('user_id'), () => <table>.id) helper to define foreign keys.

2) Prepare your DB spec (schema + relations + optional seed)

1// db/spec.ts
2import type { DBSpec } from '@vibecode-db/client'
3import { db } from './schema'
4
5export const dbSpec: DBSpec<typeof db.zodBundle.shape> = {
6  schema: db.zodBundle,
7  relations: db.relations,
8  seed: {
9    users: [
10      { id: 1, name: 'Ada',  email: 'ada@example.com' },
11      { id: 2, name: 'Alan', email: 'alan@example.com' },
12    ],
13    todos: [
14      {
15        id: 't1',
16        title: 'Wire the UI',
17        completed: false,
18        user_id: 1,
19        created_at: new Date(),
20        updated_at: new Date(),
21      },
22    ],
23  },
24}

dbSpec is passed to the client so adapters can initialize consistently.


3) (SQLite only) Provide migrations

1// db/migrations.ts
2export const migrations: string[] = [
3  // users
4  `CREATE TABLE IF NOT EXISTS "users" (
5     "id"      INTEGER PRIMARY KEY,
6     "name"    TEXT,
7     "email"   TEXT
8   );`,
9
10  // todos + FK → users.id
11  `CREATE TABLE IF NOT EXISTS "todos" (
12     "id"          TEXT PRIMARY KEY,
13     "title"       TEXT,
14     "completed"   INTEGER,
15     "created_at"  TEXT,
16     "updated_at"  TEXT,
17     "user_id"     INTEGER,
18     FOREIGN KEY("user_id") REFERENCES "users"("id")
19   );`,
20
21  // (optional) helpful index
22  `CREATE INDEX IF NOT EXISTS "todos_created_at_idx" ON "todos" ("created_at");`,
23]
  • Supply these to SQLite adapters so tables exist before queries run.

4) Create the client (pick ONE adapter per target)

Web (sqlite‑web, WASM)

1// db/client.web.ts
2import { createClient } from '@vibecode-db/client'
3import { SQLiteAdapter } from '@vibecode-db/sqlite-web'
4import { dbSpec } from './spec'
5import { migrations } from './migrations'
6
7export const db = createClient({
8  dbSpec,
9  adapter: (ctx) =>
10    new SQLiteAdapter(ctx, {
11      wasm: { wasmUrl: '/sql-wasm.wasm' }, // ensure this asset is served
12      migrations,
13      // persistence: 'opfs' | 'idb' (optional)
14    }),
15})

Expo / React Native (sqlite‑expo)

1// db/client.expo.ts
2import { createClient } from '@vibecode-db/client'
3import { SQLiteAdapter } from '@vibecode-db/sqlite-expo'
4import { dbSpec } from './spec'
5import { migrations } from './migrations'
6
7export const db = createClient({
8  dbSpec,
9  adapter: (ctx) =>
10    new SQLiteAdapter(ctx, {
11      native: { dbName: 'vibecode.db' },
12      migrations,
13      // enableForeignKeys: true, // optional
14    }),
15})

Supabase uses the same vibecode‑db client surface; wire credentials in the Supabase adapter (see Adapters → Supabase). We avoid documenting sqlite-core here (tooling-only).


5) First query

1import { db } from './db/client.web' // or .expo
2
3// Insert
4await db.from('users').insert({ id: 3, name: 'Grace', email: 'grace@example.com' })
5
6// Filter, then project
7const todos = await db
8  .from('todos')
9  .where({ user_id: 1 })
10  .select('id, title, users(name)') // nested projection from related table

Inputs/outputs are validated by Zod at runtime and are fully typed at compile time. The query builder is identical across adapters.


You’re ready

You now have: a schema with relations → a dbSpec (schema + relations + optional seed) → migrations (for SQLite) → a client bound to your environment. Next, see Configuration for per‑adapter options or Defining Your Schema to model richer relations and constraints.