Schema as code.
Everything else follows.
You write collections in TypeScript. Rebase keeps the database, the API, the types and — if you opted in — the admin panel in agreement with that one file. Clone the repo, run rebase dev, and it is already up: no Postgres to install, no Docker, no compose file to maintain.
The loop
Four commands, and then you are just writing TypeScript
01
rebase initScaffold, or adopt what exists
Creates the three packages and wires them together. Already have a database? rebase schema introspect writes collections from your tables instead.
02
rebase db pushMove the schema
Diffs your collections against the database and applies the change. db generate + db migrate when you want the SQL in version control.
03
rebase generate-sdkRegenerate the types
Writes a typed client from your collections — or from a running backend with --from <url>, in a repo that has no schema of its own.
04
rebase devRun it, with no database to install
Backend and panel together with hot reload. No DATABASE_URL? A managed Postgres starts with them — no install, no Docker, no compose file.
The project
Three folders, and one of them is optional
config/collections/ is the only place your data model is described. The backend reads it to serve APIs and generate the Drizzle schema; the panel reads the same files to render itself.
Delete frontend/ and you have a headless backend. Nothing under backend/ imports React, and the type system enforces it — an admin key in a project that never opted in is a compile error.
my-app/
├── config/collections/ # the source of truth
│ ├── index.ts
│ └── orders.ts # properties · relations · securityRules · admin
│
├── backend/ # Hono server
│ ├── src/index.ts # initializeRebaseBackend(…)
│ ├── src/schema.generated.ts # Drizzle, generated
│ └── functions/ # your own routes, auto-mounted
│
├── frontend/ # the admin panel — delete it and nothing breaks
│ └── src/App.tsx
│
└── .env # DATABASE_URL, JWT_SECRET, …Your code
Generated does not mean closed
Every generated surface has a seam you can open: callbacks around writes, your own routes on the server, your own React in the panel, and raw SQL when the abstraction is in the way.
export const orders = {
slug: "orders",
callbacks: {
onPreSave: async ({ values, context }) => {
values.total = recalculate(values.items);
return values;
},
onSaveSuccess: async ({ entity, context }) => {
await context.rebase.sql(
"INSERT INTO ledger (order_id) VALUES ($1)",
{ params: [entity.id] },
);
},
},
};Business logic runs on the server, on every write path — including writes made from the admin panel.
// backend/functions/checkout.ts
import { Hono } from "hono";
const app = new Hono();
app.post("/", async (c) => {
const { user, rebase } = c.get("rebase");
// same auth context, same RLS as every other request
return c.json({ ok: true });
});
export default app;
// → POST /api/functions/checkoutDrop a file in functions/ and it is mounted, authenticated and RLS-scoped with no registration step.
import type { PostgresCollectionConfig } from "@rebasepro/types"; export const postsCollection: PostgresCollectionConfig = { name: "Posts", slug: "posts", table: "posts", properties: { id: { name: "ID", type: "string", validation: { required: true } }, title: { name: "Title", type: "string" }, status: { name: "Status", type: "string", validation: { required: true } } } };
Rebase lets non-technical editors build database schemas visually. Any change updates the database instantly and generates type-safe AST code modifications.
drizzle-kit push:postgresResources
Declare what you need. In code, once.
Databases, buckets and topics are constructors, not YAML. Each one names its own engine — Postgres, MongoDB, Firestore, SQLite; local disk, S3, GCS, Azure — and an engine we have never heard of is spelled custom: and accepted, so it fails at the call site instead of looking like a typo.
rebase resources generates the graph a host reads before it builds anything, and --check fails CI when the committed graph and your code disagree. There is no second place to declare a bucket, which is the point: two homes for one fact means one of them is silently losing.
// config/resources.ts
import { database, bucket, topic } from "@rebasepro/types";
export const analytics = database("analytics", { engine: "mongodb" });
export const media = bucket("media", { engine: "s3", transport: "direct" });
export const signups = topic<{ userId: string }>("signups");
signups.subscription("send-welcome", async (event) => {
// durable, at-least-once, retried on its own schedule
});
await signups.publish({ userId });Publishing inside a transaction that rolls back was never published. Each subscriber retries on its own schedule, and one that gives up is a row you can look at.
Architecture
Where every piece actually runs
Your browser, your server, your database. Nothing routes through us — there is no us in the request path.
serverRow-Level SecurityRolesLifecycle Hooks@rebasepro/clientBrowser · Node · Serverless · Edgedatabase.types.tsrebase.data.*rebase.auth.*rebase.realtime.*rebase.storage.*rebase.functions.*rebase.cron.*rebase.email.*rebase.admin.*@rebasepro/cliOrchestrates schema, migrations, SDK codegen, dev server, and builds.
@rebasepro/uiSchema-as-Code · Git-Backed · Hot Reload · Self-Hostable · TypeScript End-to-End · 21 Packages
Ship it
It deploys like the Node app it is
The backend builds to a bundle and runs behind whatever you already use. The admin panel is a static SPA — serve it from the same process or from a CDN.
One process is the default and stays the default. When you outgrow it, the same bundle boots as an API, a functions tier and a worker — REBASE_ROLE per process, and which routes mount, which timers fire and who owns the schema all follow from it. Exactly one process migrates; the rest check themselves against the database and say so if they disagree.
Rebase Cloud, our managed hosting, has not launched yet. Until it does, every deployment is yours: your database, your machine, your logs.
Self-hosting guide- DockerA Dockerfile ships with every scaffolded backend.
- RailwayPush the repo, set DATABASE_URL, done.
- Fly.ioOne process, one region or several.
- Hetzner · bare metalIt is a Node server. Run it the way you run Node.
- AWS · GCP · Azure · ScalewayA deployment guide per provider in the docs.
- Your existing Hono appMount the backend as a sub-app instead of deploying it separately.
Your agents, too
Teach your coding agent the framework
rebase skills install writes packaged instructions into Claude Code, Cursor, Windsurf or Gemini, so an agent working in your repo already knows how collections, policies and the panel fit together. The MCP server gives it tools — schema introspection, migrations, queries, storage, cron — under a scoped key that row-level security still applies to.
Ready to build?
One command scaffolds the whole thing, and it runs on a managed database straight away. Name a DATABASE_URL — your own Postgres, a colleague's staging box, a Neon branch — and Rebase steps aside and uses exactly that.
