Backend-Übersicht
Übersicht
Abschnitt betitelt „Übersicht“Das Rebase-Backend ist ein auf Hono basierender Node.js-Server, der Folgendes bereitstellt:
- REST-API — Automatisch generierte CRUD-Endpunkte für jede Collection
- Authentifizierung — JWT-Tokens, OAuth- und OIDC-Anmeldung, Magic Links, Einmalcodes, MFA, API-Schlüssel, Benutzer-/Rollenverwaltung
- Storage — Datei-Upload/-Download über das lokale Dateisystem oder S3
- WebSocket — Echtzeit-Datensynchronisierung über PostgreSQL LISTEN/NOTIFY
- Entitätshistorie — Audit-Trail für jede Datenänderung
- Database Branching — Sofortige, isolierte Datenbankkopien für Dev/Staging/Testing
- Cron-Jobs — Geplante Hintergrundaufgaben mit Monitoring-Dashboard
Alles wird mit einer einzigen Funktion initialisiert:
import { initializeRebaseBackend } from "@rebasepro/server";import { createPostgresAdapter } from "@rebasepro/server-postgres";import { env } from "./env";
const instance = await initializeRebaseBackend({ app, server, collectionsDir: "./config/collections", database: createPostgresAdapter({ connection: db, schema: { tables, enums, relations } }), auth: { jwtSecret: env.JWT_SECRET, }, storage: { type: "local", basePath: "./uploads" }, history: true, enableSwagger: env.NODE_ENV !== "production"});Seit 0.25 Dieser eine Aufruf ist
eine Abkürzung über öffentliche Schritte: installUnhandledRejectionHandler verhindert,
dass eine verirrte Rejection den Prozess beendet, createRebaseBackend baut das Backend,
setDefaultBackend macht es zu dem, wofür die API auf Modulebene antwortet, und mount,
attachRealtime und start liefern es aus. Um nur einige Routengruppen einzubinden, sie
unter andere Pfade zu legen, die Timer einem anderen Prozess zu überlassen oder zwei
Backends in einem Prozess zu betreiben, rufen Sie die Schritte selbst auf. Siehe
Integration eines eigenen Servers.
Wo die einzelnen Optionen liegen
Abschnitt betitelt „Wo die einzelnen Optionen liegen“Dieser Aufruf entspricht der ejected Form – derjenigen, die Sie nach rebase eject oder in einem Custom Server selbst schreiben. Ein per Scaffolding erstelltes Projekt hat ihn nicht: Die bereitgestellte Runtime bootet das Projekt, und jede Option wird als Umgebungsvariable in .env, als Export aus config/index.ts oder als Verzeichnis, das das Bundle in rebase.json deklariert, übergeben.
Beide Pfade führen zur selben RebaseBackendConfig. Dies ist die vollständige Übersicht.
| Option | Managed Runtime |
|---|---|
basePath |
REBASE_BASE_PATH (Standard: /api) |
collections, collectionsDir |
das von rebase.json deklarierte Verzeichnis config/collections/ |
functions, functionsDir |
backend/functions/ |
crons, cronsDir |
backend/crons/ |
bootstrappers, database |
DATABASE_URL, plus eine database("<key>")-Deklaration in config/resources.ts für jede Datenbank jenseits des Standards |
auth |
JWT_SECRET, die OAUTH_*-Variablen und config/collections/users |
storage |
die STORAGE_*-Variablen, plus eine bucket("<key>")-Deklaration in config/resources.ts für jeden Bucket jenseits des Standards |
storageAuthorize |
export const storageAuthorize aus config/index.ts |
storagePublicRead |
STORAGE_PUBLIC_READ |
storageRenditionCache |
STORAGE_RENDITION_CACHE |
storageInsecureAllowAnyAuthenticated |
STORAGE_ALLOW_ANY_AUTHENTICATED |
callbacks |
export const callbacks aus config/index.ts |
history |
REBASE_HISTORY (standardmäßig aktiviert) — nur die boolesche Form |
enableSwagger |
REBASE_ENABLE_SWAGGER; nicht gesetzt bedeutet außerhalb der Produktion aktiviert |
compression |
REBASE_COMPRESSION |
maxBodySize |
REBASE_MAX_BODY_SIZE |
logging |
LOG_LEVEL |
provisionSchema, surfaces, ownership, functionsSelection, functionsUpstream |
REBASE_ROLE — siehe Split Processes |
corsHandled |
CORS wird von der Runtime aus CORS_ORIGINS installiert |
schemaVersion, runtimeVersion |
der Build stempelt beide in das Bundle ein |
app, server, provisioningDriverResult |
die Runtime erstellt sie |
Optionen ohne verwalteten Pfad
Abschnitt betitelt „Optionen ohne verwalteten Pfad“Diese besitzen weder Umgebungsvariablen noch Konfigurationsexporte. Sie sind nur über einen manuell geschriebenen initializeRebaseBackend-Aufruf erreichbar – per rebase eject oder über einen Custom Server:
rateLimit · jobs · csrf · cronPersistence · functionsTimeoutMs ·
storagePolicies · storageTriggers · baas · liveSchema · rlsAudit ·
history in seiner Objektform ({ retention })
schemaEditor wird in einem erstellten Bundle zwangsweise deaktiviert: Der Editor schreibt Quelldateien von Collections um, und ein Bundle enthält kompilierten Output.
Was erstellt wird
Abschnitt betitelt „Was erstellt wird“Nach der Initialisierung werden diese Routen gemountet:
| Pfad | Zweck |
|---|---|
/api/auth/* |
Authentifizierung (Registrierung, Login, Refresh, OAuth, Magic Links, Einmalcodes, MFA) |
/api/admin/* |
Benutzer, Rollen, Service-Schlüssel, Cron, Backups, Logs und Schema, jeweils hinter einem eigenen Scope |
/api/storage/* |
Datei-Upload, -Download und -Löschung |
/api/data/:slug |
CRUD-Operationen pro Collection (GET, POST, PATCH, DELETE) |
/api/data/:slug/:id/history |
Änderungshistorie der Entität (wenn aktiviert) |
/api/docs |
OpenAPI-Spezifikation (wenn enableSwagger: true) |
/api/swagger |
Swagger UI (Dev-Modus, wenn enableSwagger: true) |
/api/meta/contract |
Das Collection-Schema des Projekts (braucht schema:read) |
/api/meta/schema-version |
Ein Versions-String für dieses Schema (nicht authentifiziert) |
/api/functions/* |
Benutzerdefinierte Funktionsrouten (wenn functions oder functionsDir gesetzt ist) |
/api/admin/cron/* |
Cron-Job-Verwaltung (cron:read / cron:write, wenn crons oder cronsDir gesetzt ist) |
| WebSocket beim Upgrade | Echtzeit-Abonnements, auf / bei einer API unter /api |
Der Initialisierungslebenszyklus
Abschnitt betitelt „Der Initialisierungslebenszyklus“Wenn Sie initializeRebaseBackend() aufrufen, löst das Framework eine sequentielle, 5-stufige Boot-Sequenz aus:
[Start Boot] │ ▼1. ENV validation (Zod parsing of jwt, databases, cors) │ ▼2. Dynamic Collection Loading (Chokidar watches .ts files, AST parsing) │ ▼3. Database Bootstrapping (Acquires advisory lock, creates schemas/auth/helper SQL functions) │ ▼4. Service Initialization (Auth, Storage S3/Local client instances, Cron store seeding) │ ▼5. Route Mounting & Edge Loading (Hono controllers, custom functions, WebSocket binding) │ ▼[Boot Complete]Was passiert, wenn das Booten fehlschlägt
Abschnitt betitelt „Was passiert, wenn das Booten fehlschlägt“Der Boot-Vorgang schlägt lautstark fehl. Wenn die Datenbank nicht erreichbar ist, die Anmeldedaten falsch sind oder das Collection-Schema nicht angewendet werden kann, wirft initializeRebaseBackend einen Fehler, es wird nichts ausgeliefert und der Prozess wird mit 1 beendet. Es gibt keinen herabgesetzten Modus und keinen Teil-Server: Ein Container, der seine Datenbank nicht erreichen kann, startet neu, und der Fehler, der ihn beendet hat, ist der letzte Eintrag in seinen Logs.
Das ist beabsichtigt. Ein Server, der hochfährt und Logins beantwortet, während jede /api/data/*-Route fehlschlägt, ist viel schwerer zu diagnostizieren als einer, der gar nicht erst startet – und ein Orchestrator kann auf einen Crash-Loop reagieren.
Vor der ersten Abfrage prüft der Boot-Vorgang die Verbindung und gibt die Diagnose aus: den Host und Port, die nicht erreicht werden konnten, die Begründung des Treibers selbst (ECONNREFUSED, password authentication failed for user "app") sowie die Lösung. Siehe Fehlerbehebung für die vollständige Liste aller Fehlerfälle.
Sobald der Server läuft: /livez und /health
Abschnitt betitelt „Sobald der Server läuft: /livez und /health“Zwei Probes, die zwei unterschiedliche Fragen beantworten.
| Pfad | Greift auf die Datenbank zu | Antwortet |
|---|---|---|
/livez |
Nein | 200 {"status":"ok"}, solange der Prozess läuft. Verwenden Sie dies für eine Liveness-Probe. |
/health |
Ja, jede Datenquelle | 200 {"status":"ok"}, wenn jede konfigurierte Datenquelle antwortet; 503 {"status":"degraded"}, wenn eine nicht antwortet. Verwenden Sie dies für eine Readiness-Probe. |
Eine Liveness-Probe auf /health ist ein Fehler, der erwähnenswert ist: Ein kurzer Datenbank-Schluckauf würde den Orchestrator dazu veranlassen, einen ansonsten gesunden Prozess zu beenden, was einen kurzen Ausfall in eine Neustartschleife verwandeln würde.
Da /health nicht authentifiziert ist, gibt der Endpunkt das Ergebnis aus und nicht die Begründung – außerhalb der Entwicklungsumgebung nennt er lediglich, welche Datenquelle beeinträchtigt ist, und sonst nichts. Der Fehlertext des Treibers nennt Host, Port, Datenbankname und Rolle, und dies wird in die Logs geschrieben. Beide Pfade werden auch unter basePath bereitgestellt (/api/health).
Konfigurationsreferenz
Abschnitt betitelt „Konfigurationsreferenz“interface RebaseBackendConfig { // HTTP framework app: Hono; // Hono application instance server: Server; // Node.js HTTP server (for WebSocket attachment) basePath?: string; // Route prefix (default: "/api")
// Collections collections?: CollectionConfig[]; // Your collection definitions collectionsDir?: string; // Auto-load collections from a directory
// Database adapter (PostgreSQL, SQLite, etc.) database?: DatabaseAdapter;
// Authentication configuration or custom adapter auth?: RebaseAuthConfig | AuthAdapter;
// File storage storage?: BackendStorageConfig | Record<string, BackendStorageConfig>;
// Entity history history?: boolean | HistoryConfig;
// OpenAPI/Swagger enableSwagger?: boolean;
// Custom API endpoints: values, or a directory of files (not both) functions?: Record<string, FunctionApp>; // { "send-invoice": app } → /api/functions/send-invoice functionsDir?: string; // Auto-load Hono routes from a directory
// Scheduled tasks: values, or a directory of files (not both) crons?: Record<string, CronJobDefinition>; // { "nightly-report": defineCron(…) } cronsDir?: string; // Auto-load cron jobs from a directory cronPersistence?: boolean; // Write run logs to rebase.cron_logs (default: true)
// HTTP behaviour compression?: boolean; // gzip/deflate for API responses (default: true) maxBodySize?: number; // Request-body ceiling in bytes (default: 10MB; 0 disables) csrf?: { origin: string | string[] | ((origin: string) => boolean) };
// Schema editing schemaEditor?: boolean; // Force the schema-editor routes on or off
// Logging logging?: { level?: "error" | "warn" | "info" | "debug" };}Fünf davon werden leicht übersehen und verändern Verhaltensweisen, die Sie sonst nur beobachten können:
| Schlüssel | Standard | Funktion |
|---|---|---|
compression |
true |
gzip/deflate für API-Antworten, ausgehandelt über Accept-Encoding. Bereits komprimierte, gestreamte und mit no-transform versehene Payloads bleiben unberührt, ebenso Payloads unter 1 KB (gzip kostet dort mehr, als es einspart), sodass es sicher aktiviert bleiben kann – eine große JSON-Liste schrumpft typischerweise um das ~20-Fache. Setzen Sie dies auf false, wenn nginx, Cloudflare oder ein anderer vorgeschalteter Proxy bereits komprimiert, um doppelte Verarbeitung zu vermeiden. Umgebungsvariable: REBASE_COMPRESSION. |
maxBodySize |
10485760 (10MB) |
Obergrenze für Request-Bodies bei API-Routen; 0 deaktiviert sie. Storage-Uploads verwenden das separate maxFileSize (50MB) der Storage-Konfiguration, welches für diese Routen Vorrang hat. Umgebungsvariable: REBASE_MAX_BODY_SIZE. |
csrf |
deaktiviert | Opt-in. Eine BaaS-API wird von mobilen Apps, SPAs auf anderen Domains und CLI-Tools aufgerufen, von denen keines einen Origin-Header sendet, den eine feste Liste akzeptieren würde – daher ist dies standardmäßig nicht aktiviert. Aktivieren Sie es mit den Origins, die Ihre Browser-Clients verwenden. Keine Umgebungsvariable vorhanden: Führen Sie rebase eject aus, um dies zu konfigurieren. |
cronPersistence |
true |
Gibt an, ob Ausführungsprotokolle in rebase.cron_logs geschrieben werden. Bei false laufen die Jobs weiter und der Verlauf verbleibt nur im Speicher, was das Studio-Panel bei einem Neustart dann verliert. |
schemaEditor |
außerhalb der Produktion aktiv, wenn collectionsDir gesetzt ist |
Schaltet die Schema-Editor-Routen explizit ein oder aus. Der Editor schreibt Quelldateien von Collections um, benötigt also ein Verzeichnis, in das er schreiben kann – und ein erstelltes Bundle hat keines, weshalb ein Deployment diese Routen niemals besitzt. |
Der Rest von RebaseBackendConfig ist entweder auf einer eigenen Seite dokumentiert (auth, storage, jobs, callbacks, liveSchema, rlsAudit) oder als @internal markiert: bootstrappers, provisioningDriverResult, provisionSchema, corsHandled, functionsSelection, functionsUpstream und runtimeVersion werden von bootFromBundle aus der Umgebung befüllt, und deren manuelle Übergabe führt zu Konflikten mit der Runtime darüber, was dieser Prozess eigentlich darstellt.
Die Backend-Instanz
Abschnitt betitelt „Die Backend-Instanz“initializeRebaseBackend gibt das Backend zurück, das es gebaut hat, bereits eingebunden und
gestartet. Sein Typ ist RebaseBackendInstance, und das ist RebaseBackend, der Typ, den
createRebaseBackend zurückgibt:
const instance = await initializeRebaseBackend(config);
// Internal service accessinstance.driver // Default data driverinstance.driverRegistry // All drivers (for multi-database)instance.realtimeService // Default realtime serviceinstance.auth?.userService // User managementinstance.storageController // Default storageinstance.storageRegistry // All storage backendsinstance.collectionRegistry // Collection metadatainstance.history?.historyService // Entity historyinstance.cronScheduler // Cron job scheduler (when crons or cronsDir is set)instance.jobQueue // Durable job queue (when jobs are enabled)instance.setCollectionCallbacks(slug, callbacks) // Replace a collection's callbacksinstance.shutdown() // Drain and stop; also closes the server it was givenSeit 0.25 Es trägt außerdem rebase,
den In-Process-Client, fetch(request), routes, die Routengruppen als Hono-Sub-Apps,
services, seine eigenen JWT-Schlüssel, sein Zugriffsmodell, seinen API-Schlüssel-Speicher
und seine Rate-Limiter, sowie verifyCredential(token); siehe
Integration eines eigenen Servers.
Hinweis: Obwohl die
instancediese internen Dienste verfügbar macht, sollte Anwendungscode (wie benutzerdefinierte Funktionen und Cron-Jobs) dasrebase-Singleton aus@rebasepro/serververwenden, um mit der Backend-API zu interagieren. Seit 0.25 Innerhalb eines Requests, Cron-Laufs oder Jobs istrebasedas Backend, das ihn bedient; überall sonst ist es der Prozess-Standard, deninitializeRebaseBackendauf sein Backend setzt.
REST-API
Abschnitt betitelt „REST-API“Die REST-API wird automatisch aus Ihren Collections generiert. Jede Collection erhält diese Endpunkte:
| Methode | Pfad | Beschreibung |
|---|---|---|
GET |
/api/data/:slug |
Entitäten auflisten – Filtern, Sortieren, Paging und Suche erfolgen über Query-Parameter |
GET |
/api/data/:slug/count |
Wie viele Zeilen auf dieselbe Abfrage zutreffen |
GET |
/api/data/:slug/aggregate |
count/sum/avg/min/max, optional gruppiert |
GET |
/api/data/:slug/:id |
Eine einzelne Entität abrufen |
POST |
/api/data/:slug |
Eine neue Entität erstellen |
PATCH |
/api/data/:slug/:id |
Die übergebenen Felder aktualisieren |
DELETE |
/api/data/:slug/:id |
Einen Datensatz löschen |
POST |
/api/data/:slug/bulk |
Mehrere Zeilen in einer einzigen Transaktion erstellen |
PATCH |
/api/data/:slug/bulk |
Mehrere Zeilen in einer einzigen Transaktion aktualisieren |
POST |
/api/data/:slug/bulk/delete |
Mehrere Zeilen in einer einzigen Transaktion löschen |
Query-Parameter
Abschnitt betitelt „Query-Parameter“Es gibt eine Referenz dafür, und es ist nicht diese Seite. Die Seite REST-API dokumentiert beide Abfragedialekte, die der Server akzeptiert – das Format im PostgREST-Stil ?column=op.value und das JSON-Format ?where= – zusammen mit orderBy, limit/offset, include, fields, searchString und der Vektorsuche. Endpunkte ist der Index aller vom Server gemounteten Routen, einschließlich der generierten.
Ein Parameter, den der Server nicht reserviert hat, wird als Filter auf die Spalte dieses Namens interpretiert. Ein erfundener Parameter schlägt daher nicht fehl: Er stimmt stillschweigend mit nichts überein.
WebSocket
Abschnitt betitelt „WebSocket“Der WebSocket-Server dockt an denselben HTTP-Server an und bietet Echtzeit-Abonnements. Seit 0.25 Er beantwortet Upgrades auf genau einem Pfad, / bei einer API unter /api, wo sich das SDK einwählt, und überlässt jeden anderen Pfad Ihren eigenen upgrade-Listenern; unter 0.24 nimmt er jedes Upgrade auf dem Server an:
- Collection-Änderungen abonnieren — Benachrichtigungen erhalten, wenn eine beliebige Entität in einer Collection erstellt, aktualisiert oder gelöscht wird
- Entitätsänderungen abonnieren — Benachrichtigungen erhalten, wenn sich eine bestimmte Entität ändert
- Automatisches Reconnection-Handling im typisierten SDK
Das Backend verwendet intern PostgreSQL LISTEN/NOTIFY. Geben Sie für Multi-Instanz-Deployments einen connectionString in Ihrem PostgresBootstrapper an, um instanzübergreifendes Broadcasting zu aktivieren.
Fehlerbehandlung
Abschnitt betitelt „Fehlerbehandlung“Jeder Fehler – von jeder Route, in jedem Subsystem – wird in einem einheitlichen Envelope zurückgegeben:
{ "error": { "message": "Entity not found", "code": "NOT_FOUND", "requestId": "9f1c0b8e-4d2a-4e1b-9d0f-2c7a5b3e6a11" }}| Feld | Immer vorhanden | Bedeutung |
|---|---|---|
message |
ja | Geschrieben für Personen, die eine Konsole lesen. Nennt das Problem, nicht die interne Regel. |
code |
ja | SCREAMING_SNAKE_CASE und stabil. Dies ist das Feld für Verzweigungen. |
details |
nein | Strukturierte Nutzdaten, wenn sich die Ablehnung auf etwas Konkretes bezieht – eine Liste fehlerhafter Pfade, ein Satz unbekannter Felder. |
requestId |
nein | Vorhanden, wenn die Anfrage eine ID trug oder ihr eine zugewiesen wurde; spiegelt X-Request-ID wider. Geben Sie diese in einem Bug-Report an. |
Der HTTP-Status befindet sich auf der Response, nicht im Body. Verzweigen Sie basierend auf code, nicht auf message – Meldungen sind für Menschen geschrieben und können sich ändern.
Das typisierte SDK wandelt jeden dieser Fehler in einen RebaseApiError um, der status, code und details enthält – einschließlich der Fehler, die den Server gar nicht erst erreicht haben. Eine abgewiesene Verbindung, ein DNS-Fehler, CORS oder ein Verbindungsabbruch kommen als status: 0, code: "NETWORK_ERROR" an, wobei der Fehler der Runtime selbst unter cause liegt, anstatt als beliebiger Fehler, mit dem fetch gerade abbrechen wollte. Eine Antwort ohne Envelope — das 413 eines Proxys, das 502 eines Load Balancers — erhält dennoch einen code aus ihrem Status (PAYLOAD_TOO_LARGE, SERVICE_UNAVAILABLE, INTERNAL_ERROR, …), und ein außerhalb eines Browsers erstellter Client ohne baseUrl scheitert mit BASE_URL_REQUIRED statt mit einem Netzwerkfehler.
Seit 0.25 Ein Lesezugriff, der einen Erfolg ohne die erwartete Form erhält — ein 204, ein leerer Body, eine Seite ohne data-Liste —, scheitert mit UNEXPECTED_RESPONSE, statt als „keine Zeilen” aufgelöst zu werden, und createMany, updateMany, deleteMany oder batch scheitern mit INVALID_BULK_BODY, dem Code, den der Server für denselben Fehler verwendet, wenn etwas anderes als ein Array übergeben wird. Daher fängt Anwendungscode eine einzige Klasse ab:
async function setPrice(id: string, price: number) { try { return await client.data.products.update(id, { price }); } catch (e) { if (e instanceof RebaseApiError && e.code === "NOT_FOUND") return null; throw e; }}Nächste Schritte
Abschnitt betitelt „Nächste Schritte“- Authentifizierung — JWT, OAuth- und OIDC-Provider, MFA, API-Schlüssel, Benutzerverwaltung
- Storage — Lokaler und S3-Dateispeicher
- Entitäts-Callbacks — Lifecycle-Hooks und
context.data-API - Entitätshistorie — Audit-Trail
- Benutzerdefinierte Funktionen — Eigene API-Endpunkte hinzufügen
- Cron-Jobs — Geplante Hintergrundaufgaben
- Database Branching — Sofortige Datenbankkopien für Dev/Staging
- Deployment — Das Backend in Produktion bringen