Skip to content

Architecture Overview

Rebase is a full-stack platform with four layers:

┌─────────────────────────────────────────────────────────────────┐
│ Frontend Layer │
│ React Admin UI • Custom Views • Plugins • Your App │
│ @rebasepro/core • @rebasepro/ui • @rebasepro/studio │
└───────────────────────────┬─────────────────────────────────────┘
│ HTTP + WebSocket
┌─────────────────────────────────────────────────────────────────┐
│ Backend Layer │
│ Hono HTTP Server • REST API • Auth • Storage • WS │
│ @rebasepro/server-core │
└───────────────────────────┬─────────────────────────────────────┘
│ Drizzle ORM
┌─────────────────────────────────────────────────────────────────┐
│ Database Layer │
│ PostgreSQL • Tables • RLS Policies • Realtime sync │
└─────────────────────────────────────────────────────────────────┘

The backend initializes through a unified database adapter pattern. Database-specific logic is decoupled into its own package, and the adapter handles connection pooling, schema resolution, and real-time event routing automatically.

import { createPostgresAdapter } from "@rebasepro/server-postgresql";
database: createPostgresAdapter({
connectionString: process.env.DATABASE_URL!
})

Collections automatically resolve against the configured adapter through the internal dependency injection registry.

The BackendCollectionRegistry is the runtime index of all collections, their PostgreSQL tables, enums, and Drizzle relations. It’s populated at startup from your collection definitions.

Real-time sync uses PostgreSQL’s native LISTEN/NOTIFY mechanism:

  1. A data mutation happens (insert, update, delete)
  2. The backend emits a NOTIFY on a channel
  3. The RealtimeService receives the notification
  4. It broadcasts the change to all connected WebSocket clients
  5. React components re-render with the new data

For multi-instance deployments (e.g., Cloud Run with multiple replicas), provide a connectionString in your PostgresBootstrapper so all replicas share the same LISTEN connection.

Like drivers, storage backends are registered in a registry. You can have multiple storage providers (local, S3) and route different file fields to different backends using storageId.

PackageRoleUsed By
@rebasepro/typesTypeScript interfaces for collections, properties, entities, pluginsEverything
@rebasepro/backendBackend server initialization, REST API, auth, storage, WebSocketBackend
@rebasepro/clientClient SDK — HTTP transport, WebSocket, authFrontend
@rebasepro/coreReact framework — Scaffold, controllers, forms, routes, hooksFrontend
@rebasepro/uiStandalone UI component library (Tailwind v4 + Radix)Frontend
@rebasepro/authLogin views, auth controller hooks, user managementFrontend
@rebasepro/studioCollection editor, SQL console, JS console, RLS editor, storage browserFrontend
@rebasepro/cliCLI for schema generation, DB migrations, SDK generationDev tooling
@rebasepro/formexLightweight React form state managementFrontend
@rebasepro/data_enhancementAI-powered field autocompletion pluginFrontend
@rebasepro/data_import_exportCSV/JSON/Excel import and exportFrontend
@rebasepro/schema_inferenceAuto-detect schema from existing database dataBackend/CLI
  1. User opens a collection in the admin UI
  2. Client SDK sends GET /api/data/:slug + opens a WebSocket subscription
  3. Backend queries PostgreSQL via Drizzle ORM
  4. Data transformer deserializes database records into entity format
  5. Response sent to frontend, components render
  6. WebSocket keeps the view synced in real-time
  1. User edits an entity in the form
  2. beforeSave callbacks run (validation, transformation)
  3. Client SDK sends PUT /api/data/:slug/:id
  4. Backend serializes values, runs Drizzle UPDATE
  5. afterSave callbacks run (side effects)
  6. NOTIFY broadcast triggers WebSocket update to all clients
  7. If history is enabled, a snapshot is recorded