Skip to Content
BuildDatabase

Database

Every ZXCV project is a full Postgres database — not an abstraction over one. Auth, Storage, Functions, and Realtime are all built on it, and it is the source of truth for your app’s data.

You never hold a database credential. Client SDK calls go through the Data Gateway, which runs each request as a short transaction scoped to the caller and your authorization rules — so the anon key is safe to ship, and the same rules hold no matter which surface reads the data.

Create a table

Define tables in the SQL editor in the dashboard. A table is ordinary Postgres:

create table todos ( id bigint generated always as identity primary key, title text not null, is_complete boolean not null default false, inserted_at timestamptz not null default now() );

New to the schema? Read Connect to your database first, then Auth to make rows visible only to the right users.

Querying data

Query from any SDK. select is typed from your schema, so columns autocomplete and rows come back typed.

const { data, error } = await zxcv .from('todos') .select('id, title, is_complete') .eq('is_complete', false) .order('inserted_at', { ascending: false }) .limit(20)

Results are already filtered by authorization — the client only ever sees rows a rule granted. On failure error is set (null on success); the gateway maps no-rows to 404 and a denied rule to 403.

Filters and modifiers

Chain filters and modifiers to shape the query:

await zxcv .from('todos') .select('*') .in('id', [1, 2, 3]) // eq, neq, gt, gte, lt, lte, in, like, ilike, contains .order('title') // order, limit, range, single, maybeSingle .range(0, 9) // keyset-paginated under the hood

Use .single() when you expect exactly one row (errors otherwise) or .maybeSingle() when zero or one is fine.

Writing data

const { data, error } = await zxcv .from('todos') .insert({ title: 'Buy milk' }) .select() .single()

Every write is checked against your .policy rules before it touches a row.

Migrations

Schema changes are forward-only and versioned with the rest of your app. Edit in the dashboard, or push from the CLI:

zxcv db pull # sync local schema + regenerate SDK types # edit your schema zxcv db push # apply as an expand/contract migration

On publish, ZXCV takes a restore point, applies the expand migration in one transaction, and defers destructive contract steps until the new version is promoted — so a bad deploy never strands your data.

Next steps

  • Auth — row-level security with the .policy language
  • Realtime — subscribe to inserts, updates, deletes
  • SDK reference — the full query API
  • Deploy — ship schema changes safely with canaries