Skip to content

Quickstart

pnpm dlx @rebasepro/cli init my-app

This scaffolds a project with three packages. If any of collection, Studio, managed runtime, bundle or resource is new, the five-word box on Project Structure defines them.

Folder Description
frontend/ React SPA — Vite + TypeScript with the Rebase admin UI
backend/ Your custom functions and crons, plus the generated Drizzle schema. There is no server file — the published runtime boots the project
config/ Config files and collection definitions shared by both sides
  • Node.js 22.22+ — every scaffold, headless included, declares "node": ">=22.22.0"
  • pnpm (recommended) or npm

No database to install, and no Docker. rebase dev runs a managed PostgreSQL for the project, with its data under .rebase/. See Variant: use your own PostgreSQL if you would rather supply one — a local install, Neon, Supabase, or the container this scaffold ships.

init generates a ready-to-run .env at the project root with a real JWT_SECRET, a database password, and a free local database port. You don’t need to create or edit anything to get started.

pnpm install
pnpm run dev

That is the whole first run. There is no database to install and no schema step: with no DATABASE_URL set, rebase dev starts a managed PostgreSQL (PGlite) in the project directory, generates the Drizzle schema from your collections, and creates the tables at boot — including the example posts, authors and tags.

It starts both halves together:

  • Backend — REST API, auth, storage, WebSocket
  • Frontend — the panel: Rebase CMS and Rebase Studio
  • Hot reload for both

Both ports are derived from this project’s path rather than fixed, so several Rebase projects can run side by side. rebase dev prints the two URLs it bound — use those, not localhost:3001 / localhost:5173. (PORT and VITE_API_URL in .env configure rebase start, which runs the built bundle, and are ignored here.) Pin a port with rebase dev --port 3001.

Flag On What it does
--yes init Never prompt. Required when there is no terminal to answer, such as CI. It skips git init and dependency install — the interactive defaults say yes to both, so pass --git / --install if you want them
--headless init A backend with no collection files and no UI — see Backend only
--template <name> init Start from a template other than the default
--install / --no-install init Run the package manager for you, or leave it
--docker dev Use PostgreSQL in a container instead of the managed one. Since 0.25 The other commands follow it until rebase dev runs without it. A DATABASE_URL outranks it: then it starts nothing, and says so
--no-db dev Start no database at all — not the container and not the managed one. Set DATABASE_URL yourself

The managed database is a convenience, not a requirement. To point the project at a Postgres you run, uncomment DATABASE_URL in .env:

DATABASE_URL=postgresql://username:password@localhost:5432/your_database

Then start the dev servers as above. A DATABASE_URL that is set is never touched, and one pointing anywhere other than this machine is left alone entirely.

The migration commands plan changes with Atlas, the schema-migration engine Rebase plans with, which needs a second empty database to compare against. On your own database Rebase makes one beside it; on the managed one, which serves exactly one database, it starts a throwaway PGlite for the length of the command. Since 0.25 On 0.24 the migration commands refuse the managed database, and need your own. Either way:

pnpm run db:push

Boot already creates missing tables — and their RLS policies, PostgreSQL’s row-level security, which is how Rebase enforces who may read a row — additively, so db push is for what it deliberately leaves alone: any change that is not purely additive — a renamed column, a narrowed type, a removed field.

The scaffold also ships a docker-compose.yml with a PostgreSQL service, if you want a container rather than an installed Postgres:

docker compose up -d db

Introspect an Existing Database (Optional)

Section titled “Introspect an Existing Database (Optional)”

If you are connecting to an existing database with pre-existing tables, you can introspect it to automatically generate your TypeScript collection files:

pnpm rebase schema introspect

This will analyze your database tables and generate corresponding TypeScript files in config/collections/ so you don’t have to write them manually.

When you open the frontend URL rebase dev printed, you’ll see the login screen. The first user to register automatically becomes an admin — this is the bootstrap flow.

  1. The screen opens on Welcome!, the form for that first account
  2. Enter your email and password, then click Create account
  3. You’re in — with full admin access

rebase init also wrote REBASE_ADMIN_EMAIL and a generated REBASE_ADMIN_PASSWORD into .env. Those are not your credentials here: rebase dev ignores them and says so at boot. They belong to a production boot — docker compose up, or anything with NODE_ENV=production — where this bootstrap window is closed, because the server answers on a hostname before you have typed anything. See Your first admin.

Open config/collections/ and create a new file. Export the collection as the default export — that’s what the backend and the admin panel load from each file in the directory. The table name is optional: it defaults to the slug, so set it only when they differ:

import { defineCollection } from "@rebasepro/cms-types";
const productsCollection = defineCollection({
slug: "products",
name: "Products",
properties: {
name: {
type: "string",
name: "Name",
validation: { required: true }
},
price: {
type: "number",
name: "Price",
validation: { required: true, min: 0 }
},
description: {
type: "string",
name: "Description",
admin: { multiline: true }
},
active: {
type: "boolean",
name: "Active",
defaultValue: true
},
createdAt: {
type: "date",
name: "Created At",
autoValue: "on_create"
}
}
});
export default productsCollection;

Then add it to the collections array in config/collections/index.ts. That array is a sort order, not a registry: every collection file in the directory is loaded — by the backend, the admin panel and the SDK generator — whether the array lists it or not, and the array decides where it appears in the navigation. To remove a collection, delete its file:

// ...existing imports
import productsCollection from "./products.js";
export const collections = [
postsCollection, authorsCollection, tagsCollection, usersCollection, productsCollection
];

Save the file. That is the whole step: rebase dev regenerates backend/src/schema.generated.ts and the SDK types in generated/sdk/ from your collections, restarts the backend, and boot creates the new table — so your Products collection appears in the navigation.

On 0.23, rebase dev regenerates the schema only, so run rebase generate-sdk after a collection change before importing its types.

The same is true of a property added to a collection you already have: save, and the column is there.

rebase db push is for the changes boot deliberately leaves alone — a renamed column, a narrowed type, a removed field:

pnpm run db:push

Since 0.25 It runs on the managed database as on your own. On 0.24 it needs your own PostgreSQL.

Command Description
rebase schema generate Generate the Drizzle schema from your TypeScript collections. No database needed — rebase dev runs it for you
rebase schema introspect Generate TypeScript collections from an existing database
rebase db push Push schema changes directly to the database, the managed one included
rebase db generate Generate SQL migration files
rebase db migrate Run pending migrations

Since 0.25 The three db commands work on the managed database. On 0.24 they need your own PostgreSQL.