Salta ai contenuti

Struttura del progetto

Un progetto iniziale Rebase presenta tre pacchetti interconnessi:

my-app/
├── .env # Generated for you: JWT_SECRET, a database password, a free port
├── rebase.json # Which apps this repository contains, and how each is built
├── package.json # Root workspace config
├── tsconfig.json # Typechecks scripts/; each workspace typechecks on its own
├── docker-compose.yml # Self-hosting: Postgres + the published runtime image
├── scripts/
│ └── example.ts # The SDK end to end against the running backend: `pnpm example`
│
├── config/ # Shared by the backend and the admin panel
│ ├── index.ts # Re-exports `collections`; the runtime reads `storageAuthorize` from here
│ ├── collections/ # Your data model
│ │ ├── index.ts # Sort order (`collections`) and `defaultSecurityRules` — not a registry
│ │ ├── posts.ts # Example collections
│ │ └── users.ts # The auth collection
│ ├── resources.ts # What this project needs from wherever it runs
│ ├── storage.ts # Who may read, write and list files
│ └── cms.d.ts # One line that makes the `admin` block legal here
│
├── backend/
│ ├── functions/ # Custom API routes, auto-mounted at /api/functions/<name>
│ │ └── hello.ts
│ └── src/
│ └── schema.generated.ts # Drizzle schema, regenerated from your collections
│
└── frontend/ # The admin panel (React + Vite)
├── src/App.tsx
├── src/main.tsx
└── vite.config.ts

tsc --noEmit alla radice controlla scripts/; in config/, backend/ o frontend/ controlla quel workspace. Da 0.25 Su 0.24 la radice non ha un tsconfig.json, e tsc lì stampa il proprio aiuto e non controlla nulla.

Il frontend è una tipica applicazione Vite + React + TypeScript. Il file principale è App.tsx, che collega tra loro tutti i controller Rebase:

import React from "react";
import "@fontsource/jetbrains-mono";
import "@fontsource-variable/inter";
import "@fontsource-variable/instrument-sans";
import { Rebase, RebaseAuth, useRebaseAuthController } from "@rebasepro/app";
import { RebaseCMS, RebaseShell } from "@rebasepro/cms";
import { ErrorBoundary } from "@rebasepro/ui";
import { RebaseStudio } from "@rebasepro/studio";
import { createRebaseClient } from "@rebasepro/client";
import { collections } from "virtual:rebase-collections";
// `rebase dev` injects VITE_API_URL with the port it actually bound, and that
// port is derived from this project's path rather than fixed — so a
// `http://localhost:3001` fallback here names a port nothing is listening on.
// A deployed build serves the admin from the same origin as the API, where an
// empty value is exactly what you want.
const API_URL = import.meta.env.VITE_API_URL;
const GOOGLE_CLIENT_ID = import.meta.env.VITE_GOOGLE_CLIENT_ID;
export function App() {
const rebaseClient = React.useMemo(() => createRebaseClient({
baseUrl: API_URL,
// Store the refresh token in an httpOnly cookie (XSS-safe) rather than
// localStorage. The backend issues it via `auth.cookieAuth`.
auth: { authFlowMode: "cookie" }
}), []);
const authController = useRebaseAuthController({
client: rebaseClient,
googleClientId: GOOGLE_CLIENT_ID
});
return (
<ErrorBoundary fullPage>
<Rebase
client={rebaseClient}
authController={authController}
>
{/* The sign-in screen. On its own this changes nothing —
it is where you pass `loginView` to replace it. */}
<RebaseAuth />
<RebaseCMS
collections={collections}
/>
<RebaseStudio/>
<RebaseShell title="Rebase"/>
</Rebase>
</ErrorBoundary>
);
}

main.tsx lo monta sotto un basename di react-router ricavato da import.meta.env.BASE_URL, che rebase build imposta a partire dal path dichiarato da questa app in rebase.json — in questo modo gli asset, il router e il server concordano su un unico valore senza doverlo scrivere tre volte.

  • createRebaseClient — Crea il client SDK che gestisce le richieste HTTP, le connessioni WebSocket e la gestione dei token di autenticazione
  • virtual:rebase-collections — Un plugin Vite che importa automaticamente le collection condivise al momento della compilazione
  • useRebaseAuthController — Mantiene l’utente autenticato e il ciclo di vita del token, ed è ciò che <Rebase> distribuisce a tutto ciò che si trova al suo interno

Non c’è alcun file server da leggere, ed è proprio così che è stato progettato: un progetto creato tramite scaffold dichiara runtime: "managed", quindi l’immagine pubblicata rebasepro/server avvia il tuo progetto. Ciò che contiene backend/ è il codice rilevato dal runtime:

Percorso Cos’è
backend/functions/ Route personalizzate, montate automaticamente su /api/functions/<filename>
backend/crons/ Job pianificati, individuati allo stesso modo (crea la cartella quando ne hai bisogno)
backend/src/schema.generated.ts Lo schema Drizzle delle tue collection: scritto da rebase init e rigenerato a ogni rebase dev e rebase build

Il runtime configura:

  • REST API su /api/data/* — CRUD generato per ogni collection
  • Autenticazione su /api/auth/* — registrazione, login, refresh, OAuth
  • Storage su /api/storage/* — upload e download
  • WebSocket — sincronizzazione in tempo reale tramite LISTEN/NOTIFY di Postgres
  • Funzioni e cron, dalle directory sopra indicate

La configurazione proviene da rebase.json, dalla directory config/ e dalle variabili d’ambiente. Consulta Ambiente e configurazione.

rebase build trasforma tutto questo in un bundle — le collection compilate, le funzioni e i cron oltre a un manifest — che il runtime gestito avvia. Nessuna parte del bundle viene scritta a mano; se vuoi vederne uno, Runtime e bundle ne descrive il contenuto.

Il pannello fornito dal frontend ha due parti. Studio è quella per gli sviluppatori — l’editor di schemi, la console SQL, il browser delle policy RLS — ed è accessibile tramite il selettore nel drawer, non come deployment separato. Consulta Studio.

Se desideri invece prendere il controllo del processo — con middleware, route e logica di autenticazione personalizzati — esegui rebase eject. Tutto ciò che segue questo paragrafo si applica solo dopo un eject: un progetto generato tramite scaffold non possiede nessuno di questi file e nulla al suo interno chiama initializeRebaseBackend. Il comando genera un entry point che chiama direttamente initializeRebaseBackend, oltre a un Dockerfile e a un file compose per la compilazione; da quel momento in poi sarai tu a gestire il server e gli aggiornamenti del runtime della piattaforma non interesseranno più il progetto. Quest’area è documentata in Integrazione server personalizzato.

Le collection rappresentano l’unica fonte di verità per il tuo modello di dati. Sono definite in TypeScript e utilizzate sia dal frontend (per la generazione dell’interfaccia utente) che dal backend (per la generazione dello schema e il routing delle API).

import { defineCollection } from "@rebasepro/cms-types";
const productsCollection = defineCollection({
slug: "products",
name: "Products",
properties: {
name: { type: "string", name: "Name" },
price: { type: "number", name: "Price" }
}
});
// The default export is what the loader picks up — every file in this directory
// is loaded, listed in index.ts or not, and every collection in the scaffold is
// written this way.
export default productsCollection;

Lo slug diventa il percorso URL nell’interfaccia di amministrazione e l’endpoint dell’API REST (/api/data/products), e il nome della tabella PostgreSQL corrisponde ad esso per impostazione predefinita. Aggiungi table solo se differiscono.

  1. Tu definisci le collection in config/collections/
  2. Il backend le legge per generare gli schemi Drizzle e registrare le route REST
  3. Il frontend le legge (tramite il plugin Vite) per renderizzare tabelle, moduli e navigazione
  4. La CLI le legge per generare lo schema Drizzle (rebase schema generate), i tipi dell’SDK (rebase generate-sdk) e, a partire da quello schema, i file di migrazione (rebase db generate)

Mentre rebase dev è in esecuzione, il salvataggio di un file all’interno di config/collections/ rigenera backend/src/schema.generated.ts e i tipi dell’SDK in generated/sdk/ e riavvia il backend; all’avvio vengono create le tabelle e le colonne mancanti. Al di fuori di rebase dev, gli stessi passaggi corrispondono a rebase schema generate e rebase generate-sdk.

Sulla 0.23, rebase dev rigenera solo lo schema, e rebase generate-sdk resta un passaggio separato.