Schema as Code
The Core Idea
Section titled “The Core Idea”In Rebase, your TypeScript collection definitions are the single source of truth. From one set of TypeScript objects, Rebase generates:
- PostgreSQL tables via Drizzle ORM schema generation
- CRUD UI — forms, tables, validation, field types
- REST API endpoints with filtering, sorting, and pagination
- Client SDK — type-safe data operations
- RLS policies — Row Level Security in Postgres
This means your schema is:
- Version controlled — every change is a git commit
- Type-safe — TypeScript catches errors at compile time
- Reviewable — schema changes go through pull requests
- Portable — the same definition works across frontend, backend, and CLI
Visual Editing with AST Manipulation
Section titled “Visual Editing with AST Manipulation”Rebase also provides a visual collection editor in Studio mode. When a non-developer uses the visual editor to add a field:
- The Studio does not directly modify the database
- Instead, it uses ts-morph to parse your TypeScript source file as an AST
- It inserts the new property definition precisely into the
propertiesblock - All existing code, callbacks, and custom logic are preserved untouched
- The file is saved, triggering hot reload
This “UI as Code Generator” approach means visual edits produce the same clean TypeScript a developer would write by hand.
Schema Generation Pipeline
Section titled “Schema Generation Pipeline”TypeScript Collections │ ▼ rebase schema generate │ ▼ Drizzle Schema (schema.generated.ts) │ ▼ rebase db generate │ ▼ SQL Migration Files │ ▼ rebase db migrate │ ▼ PostgreSQL TablesExample
Section titled “Example”Given this collection:
const productsCollection: EntityCollection = { slug: "products", table: "products", properties: { name: { type: "string", name: "Name", validation: { required: true } }, price: { type: "number", name: "Price", columnType: "numeric" }, active: { type: "boolean", name: "Active", defaultValue: true }, created_at: { type: "date", name: "Created", autoValue: "on_create" } }};Rebase generates this Drizzle schema:
// schema.generated.tsimport { pgTable, varchar, numeric, boolean, timestamp } from "drizzle-orm/pg-core";
export const products = pgTable("products", { id: serial("id").primaryKey(), name: varchar("name").notNull(), price: numeric("price"), active: boolean("active").default(true), created_at: timestamp("created_at").defaultNow()});Which produces this SQL:
CREATE TABLE products ( id SERIAL PRIMARY KEY, name VARCHAR NOT NULL, price NUMERIC, active BOOLEAN DEFAULT true, created_at TIMESTAMP DEFAULT NOW());Safety & Unmapped Database Objects
Section titled “Safety & Unmapped Database Objects”When Rebase updates the database schema, it maps TypeScript collection definitions to database tables. To ensure that unmapped database objects (e.g., tables, views, enums) are never dropped or modified, Rebase implements several layers of safety:
- Strict Table Filtering (
tablesFilter): The generated Drizzle configuration dynamically restricts synchronization to only the tables exported in the generated schema. Any unrecognized tables or legacy systems’ tables existing in the database are ignored by the sync engine. - Schema Restrictions (
schemaFilter): The DB sync is restricted exclusively to thepublicschema. Internal database tables, custom schemas, and extension-specific tables are left untouched. - Roles and Extension Protection: Drizzle is configured not to manage database roles (
entities.roles: false) or helper tables from extensions like PostGIS. - Interactive Dev Mode Confirmation: When running
rebase db pushin development, the CLI executes with--strictand--verboseflags, which ensures that developers must explicitly review and approve any destructive SQL actions before they are executed.
Next Steps
Section titled “Next Steps”- Collections — Full collection configuration reference
- Properties — Detailed column type mappings