Schema Generation
Overview
Section titled “Overview”Rebase uses a schema-as-code pipeline where your TypeScript collection definitions are the single source of truth. The CLI transforms them through a deterministic pipeline:
Collections (TypeScript) → Drizzle Schema → SQL Migrations → PostgreSQLThis page covers every CLI command involved in that pipeline.
The Pipeline
Section titled “The Pipeline”1. Collections → Drizzle Schema
Section titled “1. Collections → Drizzle Schema”Your collection definitions in config/collections/ describe tables, columns, types, relations, and enums. The schema generate command reads these and outputs a Drizzle ORM schema file.
2. Drizzle Schema → Migrations
Section titled “2. Drizzle Schema → Migrations”From the generated Drizzle schema, db generate diffs against the current database state and produces timestamped SQL migration files.
3. Migrations → PostgreSQL
Section titled “3. Migrations → PostgreSQL”The db migrate command applies pending migrations to your PostgreSQL database.
Commands
Section titled “Commands”rebase schema generate
Section titled “rebase schema generate”Generate a Drizzle ORM schema file from your collection definitions:
rebase schema generateWhat it does:
- Reads all collections from
config/collections/ - Generates
backend/src/schema.generated.tswith Drizzle table definitions, enums, and relations
Options:
| Flag | Description |
|---|---|
--collections, -c |
Path to collections directory (default: config/collections/) |
--output, -o |
Output path for the generated schema file |
--watch, -w |
Watch for changes and regenerate automatically |
Watch mode is useful during development — edit a collection file and the schema regenerates instantly:
rebase schema generate --watchrebase schema introspect
Section titled “rebase schema introspect”Reverse-engineer collection definitions from an existing PostgreSQL database:
rebase schema introspectWhat it does:
- Connects to your database (using the connection string from your
.env) - Inspects all tables, columns, types, and foreign keys
- Generates collection definition files
Options:
| Flag | Description |
|---|---|
--output, -o |
Output directory for generated collection files |
This is useful when adopting Rebase on an existing database — introspect first, then customize the generated collections.
What the generated files look like
Introspection writes collections against defineCollection, which keeps the property keys literal — so propertiesOrder, listProperties, sort and display.title complete over your own column names, and a key left behind by a renamed column is a compile error rather than a line that quietly does nothing. Which one it imports depends on what your project depends on, and is decided per run:
| Your project declares | Generated collections use | admin block |
|---|---|---|
@rebasepro/cms-types (a project with the panel) |
defineCollection from @rebasepro/cms-types |
yes |
@rebasepro/common (a --headless project) |
defineCollection from @rebasepro/common |
no |
| neither | a PostgresCollectionConfig annotation, with a warning |
no |
A headless project has no admin panel, and the core types declare no admin field at all — so the block is not emitted there. It is presentation (icon, propertiesOrder, multiline); nothing about the schema, the API or your data depends on it.
rebase db push
Section titled “rebase db push”Push schema changes directly to the database without migration files:
rebase db pushWhat it does:
- Reads the generated Drizzle schema
- Applies changes directly to the database (CREATE, ALTER, DROP)
- Applies your collections’ RLS policies, and removes policies an earlier push superseded
- Does not create migration files
The files it generates on the way, all under drizzle/:
| File | Holds |
|---|---|
schema.sql |
Tables, columns, constraints and indexes — Atlas’s desired state, and the only one it diffs |
policies.sql |
The RLS policies your securityRules compile to |
search.sql |
The full-text search functions and generated columns, for collections with a search block |
vector.sql |
pgvector extensions and ANN indexes |
triggers.sql |
rebase.set_updated_at() and the BEFORE UPDATE triggers behind autoValue: "on_update" |
Atlas manages the first and nothing else, so db push and the boot-time schema
ensure apply the other four themselves. A migration-only deployment — one
that runs db migrate and never db push — has to fold those four into a
migration by hand; db generate says so when a change is invisible to Atlas.
rebase db generate
Section titled “rebase db generate”Generate SQL migration files from schema changes:
rebase db generateWhat it does:
- Compares the Drizzle schema against the current database state
- Produces timestamped SQL migration files in the
drizzle/directory - Files can be reviewed, edited, and committed to version control
The generated migrations are plain SQL files — you can inspect and modify them before applying.
rebase db migrate
Section titled “rebase db migrate”Run all pending migrations:
rebase db migrateWhat it does:
- Reads the
drizzle/directory for unapplied migrations - Applies them in order to the database
- Tracks which migrations have been applied
Baselining a database Rebase has already booted
Section titled “Baselining a database Rebase has already booted”Every Rebase boot ensures the schema, and rebase db push applies it directly. So a database that has ever run either one already has the tables and types the first migration would create, and rebase db migrate stops on pq: type "posts_status" already exists (42710).
Nothing is wrong with the migration — the database was provisioned another way. Record where it already is, then migrate normally:
rebase db migrate --baseline 20260906101530rebase db migrateThe version is the numeric prefix of the migration file that describes what is in the database now. That migration and everything before it are recorded as applied; everything after it runs. On a database nothing has ever booted against, no baseline is needed — migrate straight away.
rebase db branch
Section titled “rebase db branch”Database branching for parallel development:
rebase db branch create feature_authrebase db branch switch feature_authrebase db branch listrebase db branch switch --off
rebase db branch info feature_authrebase db branch delete feature_authrebase db branch switch (and switch --off) and rebase db branch prune
are new. create, list, info and delete shipped in 0.17.
Each branch is a full copy of the database made with CREATE DATABASE ... TEMPLATE,
so it needs a real PostgreSQL server and it costs the same disk as its source.
rebase doctor
Section titled “rebase doctor”Detect three-way drift between your collection definitions, the generated Drizzle schema, and the live PostgreSQL database:
rebase doctorWhat it checks:
- Collections ↔ Generated schema — are they in sync?
- Generated schema ↔ Database — are there unapplied changes?
- Collections ↔ Database — is there any unexpected drift?
Run doctor whenever something feels out of sync. It pinpoints exactly where the mismatch is.
The database comparison needs DATABASE_URL (or ADMIN_CONNECTION_STRING). Without one, that phase is reported as skipped rather than passing, and the run never closes with “All schemas are in sync” — a check that did not happen is not a clean bill of health.
rebase generate-sdk
Section titled “rebase generate-sdk”Generate a typed client SDK from your collection definitions:
rebase generate-sdkWhat it does:
- Reads collections from
config/collections/(supportsindex.tsbarrel exports or individual files) - Generates TypeScript types for all entities in
generated/sdk/ - Produces a
database.types.tsfile for use withcreateRebaseClient<Database>()
Options:
| Flag | Description |
|---|---|
-c, --collections-dir |
Path to the collections directory (default: config/collections/) |
-o, --output |
Output directory for the SDK (default: generated/sdk/) |
--from <link|url> |
Read the schema from a running project instead of local source. link uses this checkout’s linked project. |
--token |
Bearer token for the contract endpoint (default: $REBASE_SERVICE_KEY) |
--from is what lets a repository that contains no collections — a separate
frontend, a second web app, a mobile app — generate a typed client from the
project it talks to. REBASE_SERVICE_KEY is only sent to the project this
checkout is linked to; pass --token explicitly for any other host.
Usage after generation:
import { createRebaseClient } from "@rebasepro/client";import { collectionsDictionary, type Database } from "./generated/sdk/database.types";
const client = createRebaseClient<Database>({ baseUrl: import.meta.env.VITE_API_URL, collections: collectionsDictionary,});
// Full type safety and autocompleteconst { data } = await client.data.products.find();Field names in the generated types are the ones the API serves. A field’s wire
name is its property key, and the API is camelCase throughout: introspection
generates a created_at column as a createdAt property carrying
columnName: "created_at", and serves it as row.createdAt. The column itself
is untouched. The collection accessor is turned into a property name the same
way (my-notes → client.data.myNotes), which is what collectionsDictionary
maps back to the slug.
Development Workflow
Section titled “Development Workflow”The fast-iteration workflow for development:
# 1. Edit your collection in config/collections/# 2. Generate the Drizzle schemarebase schema generate
# 3. Push directly to dev databaserebase db pushProduction Workflow
Section titled “Production Workflow”The safe, reviewable workflow for production:
# 1. Edit your collection in config/collections/# 2. Generate the Drizzle schemarebase schema generate
# 3. Generate SQL migration filesrebase db generate
# 4. Review the generated SQL in drizzle/# 5. Commit the migration to version controlgit add drizzle/
# 6. Apply in production# A database Rebase has already booted needs a baseline the first time —# see "Baselining a database Rebase has already booted" above.rebase db migrateTroubleshooting
Section titled “Troubleshooting”| Symptom | Solution |
|---|---|
Could not detect an active database plugin |
Install @rebasepro/server-postgres in backend/package.json |
| Schema file not updating | Check the --collections path points to the right directory |
| Migration shows unexpected changes | Run rebase doctor to identify drift |
db push fails on production |
Use db generate + db migrate instead |
db migrate fails with already exists (42710) |
Boot or db push already provisioned the schema — record it with rebase db migrate --baseline <version> |
Next Steps
Section titled “Next Steps”- Collections — Define your data model
- CLI Reference — All CLI commands
- Client SDK — Use the generated SDK