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 hoodUse .single() when you expect exactly one row (errors otherwise) or
.maybeSingle() when zero or one is fine.
Writing data
Insert
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 migrationOn 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
.policylanguage - Realtime — subscribe to inserts, updates, deletes
- SDK reference — the full query API
- Deploy — ship schema changes safely with canaries