Salta ai contenuti

Generazione dello Schema

Rebase usa una pipeline schema-come-codice in cui le tue definizioni di collezioni TypeScript sono l’unica fonte di verità. La CLI le trasforma attraverso una pipeline deterministica:

Collections (TypeScript) → Drizzle Schema → SQL Migrations → PostgreSQL

Questa pagina copre ogni comando CLI coinvolto in quella pipeline.

Le tue definizioni di collezioni in config/collections/ descrivono tabelle, colonne, tipi, relazioni ed enum. Il comando schema generate le legge e produce un file di schema Drizzle ORM.

Dallo schema Drizzle generato, db generate confronta con le migrazioni già presenti in backend/drizzle/migrations/, rieseguite su un database scratch, e produce file di migrazione SQL con timestamp. Non legge mai il tuo database: una colonna che l’avvio o db push hanno già aggiunto risulta comunque come ADD COLUMN.

Il comando db migrate applica le migrazioni in sospeso al tuo database PostgreSQL.

Genera un file di schema Drizzle ORM dalle tue definizioni di collezioni:

rebase schema generate

Cosa fa:

  • Legge tutte le collezioni da config/collections/
  • Genera backend/src/schema.generated.ts con le definizioni di tabelle, enum e relazioni di Drizzle

Opzioni:

Flag Descrizione
--collections, -c Directory delle collezioni (predefinito: config/collections/); i percorsi relativi vengono risolti dalla posizione in cui esegui il comando
--output, -o Percorso di output per il file di schema generato; i percorsi relativi vengono risolti dalla posizione in cui esegui il comando
--watch, -w Osservare le modifiche e rigenerare automaticamente

La modalità watch è utile durante lo sviluppo — modifica un file di collezione e lo schema si rigenera istantaneamente:

rebase schema generate --watch

Verifica se lo schema generato corrisponde ancora alle collezioni:

rebase schema stale # report, and exit 1 if anything is stale
rebase schema stale --fix # regenerate the file instead

Cosa fa:

  • Produce ciò che generano le collezioni in config/collections/ e lo confronta, dichiarazione per dichiarazione, con backend/src/schema.generated.ts
  • Nomina ogni differenza — una tabella mancante, una colonna con tipo cambiato, un’etichetta di enum eliminata, o chiavi esterne nominate come le derivava una release precedente — ed esce con un codice diverso da zero
  • Con --fix, rigenera il file invece di riportare

rebase dev mantiene il file aggiornato da sé, quindi questo è il controllo per una build o per un passaggio di CI. Accetta --collections e --output come rebase schema generate.

Esegui il reverse engineering delle definizioni di collezioni da un database PostgreSQL esistente:

rebase schema introspect

Cosa fa:

  • Si connette al database di questo progetto, quello che stampa rebase db url (se è quello gestito, viene avviato); --database-url ne indica un altro e --docker usa quello di compose
  • Ispeziona tutte le tabelle, colonne, tipi e chiavi esterne
  • Genera i file di definizione delle collezioni
  • Legge le policy di row-level security di ogni tabella nelle securityRules della sua collezione: nome, comando, modalità, elenco TO, USING e WITH CHECK. Le policy <table>_default_* che Rebase aggiunge da sé restano fuori. Così il db push che segue ricrea ogni policy com’era. Su 0.24 le policy restano fuori dai file, e un push può eliminarne una il cui nome ha la forma di una policy generata (<table>_<op>_<hash>)
  • Legge ogni espressione (il USING di una policy, un CHECK, il default di una colonna) con il search_path che usa il server, public, così una funzione nello schema rebase viene scritta rebase.roles(). Con una connessione come ruolo chiamato rebase, il percorso predefinito metteva quello schema per primo e la regola tornava come roles(), che il push non riusciva a trovare
  • Mantiene le policy di una giunzione many-to-many. Le policy <junction>_default_edge_write_* di una giunzione derivano dalle regole di aggiornamento della collezione che dichiara la relazione, e una giunzione può ereditare solo una regola che sa leggere. Una regola di ruolo torna come l’SQL che Postgres memorizza per essa, string_to_array(rebase.roles(), ','::text) && ARRAY['admin'::text], e questo si legge di nuovo come la regola di ruolo, quindi il push deriva le stesse policy. Su 0.25.0 veniva letta come SQL grezzo: il dry run elencava le tre policy come policy che nessuna collezione descrive, il push le lasciava in vigore, e una modifica successiva alla regola non le raggiungeva più
  • Scrive disableDefaultPolicies: true su una tabella che ha la row-level security attiva e non possiede l’intera baseline admin, così il push non aggiunge alcuna policy <table>_default_admin_* e l’accesso alla tabella resta com’era; qualsiasi parte della baseline che la tabella ha viene scritta come regola. Elimina la riga per dare al server e agli admin ogni riga. Una tabella con la RLS disattivata mantiene la baseline, e l’introspezione dice, una sola volta, quali sono: il push attiva la RLS qualunque cosa dica una collezione. Su 0.24 il push dopo un’introspezione aggiungeva la baseline a ogni tabella
  • Genera come entità nella navigazione una tabella le cui policy danno a ogni utente le proprie righe (leggono rebase.uid(), app.uid o il JWT), per quanto poche righe abbia. Su 0.24 una tabella piccola è un elenco di codici, e una tabella posseduta tramite un’altra è nascosta come suo figlio
  • Contro un database non in esecuzione, stampa una sola volta lo stesso banner di db push e non crea nulla. Stampa la directory di output come percorso assoluto
  • Lascia nel database i vincoli CHECK ed EXCLUDE di una tabella, e il db push successivo li mantiene. Una collezione non può dichiarare né gli uni né gli altri; un CHECK che l’introspezione sa leggere (total >= 0) diventa anche la validation della proprietà. Su 0.24 quel push li eliminava

Opzioni:

Flag Descrizione
--output, -o Directory di output per i file di collezione generati
--schema Lo schema Postgres da leggere (predefinito: public)
--force, -f Sovrascrive i file di collezione già esistenti; senza, un file esistente viene saltato
--database-url Legge questo database invece di quello del progetto
--docker Legge il database di compose, avviandolo se necessario
--data-inference / --no-data-inference Campiona fino a 100 righe per tabella per rilevare formati, vincoli e impostazioni dell’interfaccia, oppure legge solo la struttura. Senza nessuno dei due lo chiede, e senza un terminale legge solo la struttura

Questo è utile quando si adotta Rebase su un database esistente — prima esegui l’introspezione, poi personalizza le collezioni generate.

Introspezione, poi push, non cambia nulla. Le proprietà generate dicono esattamente cos’è ogni colonna — columnType, precision/scale, defaultValue, required, l’isId di una chiave ("increment" per un’identity intera, columnType: "serial" per una serial, "manual" per una chiave senza default), l’onDelete di una relazione — e il suo index: false dove al database manca l’indice che Rebase deriverebbe sulla colonna della chiave, dato che un belongsTo è indicizzato per impostazione predefinita. Dove nessun indice serve la colonna, la riga lo dice (eliminala per farne costruire uno); dove un altro indice inizia già con essa (orders_customer_id_idx, uno composito, uno univoco), la riga nomina quell’indice, e senza quella riga il push ne costruirebbe un secondo. Su 0.24 diceva di eliminare la riga in ogni caso — e il blocco search di una collezione letto a ritroso dalla colonna che ha costruito — quindi rebase db push --dry-run eseguito subito dopo un’introspezione non pianifica alcuna modifica. L’eccezione è una tabella con la row-level security disattivata: il push la attiva, e il dry run lo elenca insieme alla baseline admin che aggiunge. Dove nessuna proprietà può rappresentare una colonna — un timestamp senza time zone, un interval, un inet, un tipo enum che non si chiama <table>_<column>, un default come CURRENT_DATE — l’introspezione lo segnala, colonna per colonna, sul terminale e in testa al file, con ciò che un push le farebbe e, quando esiste, l’istruzione che fa coincidere le due cose (ALTER TYPE "mood" RENAME TO "customers_current_mood";). Una tabella con chiave su più di una colonna torna come una chiave composita, con il suo isId su ogni colonna della chiave. Viene esclusa, con il relativo motivo, quando una colonna della chiave non può portare isId (un timestamp, per esempio) o la chiave esterna di un’altra tabella punta a essa, cosa che una relazione su una sola colonna non può fare; db push lascia stare una tabella che non è una collezione. Sulla 0.23 il push eseguito subito dopo un’introspezione può ancora pianificare cambi di tipo, default rimossi e NOT NULL, e una chiave id fantasma.

Come sono fatti i file generati

L’introspezione scrive le collezioni con defineCollection, che mantiene le chiavi delle proprietà letterali — così propertiesOrder, listProperties, sort e display.title si completano sui nomi delle tue colonne, e una chiave rimasta dopo la ridenominazione di una colonna è un errore di compilazione anziché una riga che silenziosamente non fa nulla. Quale importare dipende da ciò da cui dipende il tuo progetto, ed è deciso a ogni esecuzione:

Il tuo progetto dichiara Le collezioni generate usano Blocco admin
@rebasepro/cms-types (un progetto con il pannello) defineCollection da @rebasepro/cms-types sì
@rebasepro/common (un progetto --headless) defineCollection da @rebasepro/common no
nessuno dei due un’annotazione PostgresCollectionConfig, con un avviso no

Un progetto headless non ha un pannello di amministrazione, e i tipi core non dichiarano alcun campo admin: per questo il blocco lì non viene emesso. È presentazione (icon, propertiesOrder, multiline); nulla nello schema, nell’API o nei tuoi dati ne dipende.

Applica le modifiche allo schema direttamente al database senza file di migrazione:

rebase db push

Cosa fa:

  • Legge lo schema Drizzle generato
  • Applica le modifiche direttamente al database (CREATE, ALTER, DROP)
  • Esegue prima il piano in modalità dry run e si ferma prima di tutto ciò che distrugge dati: una tabella, colonna, schema, vista o tipo eliminati, un TRUNCATE, un cambio di tipo di colonna che può perdere valori (timestamptz → date, numeric → integer), o un cambio di chiave primaria: (id) → (id, locale) dà a ogni riga un nuovo id, così link, chiavi esterne e id salvati altrove non trovano più la loro riga. Si ferma anche prima dell’eliminazione di un vincolo CHECK o EXCLUDE che il database contiene: nessuna riga va persa, ma da quel momento il database accetta righe che rifiutava. In un terminale chiede conferma, dove Ctrl-C o Ctrl-D rispondono no, altrimenti rifiuta; --allow-destructive applica comunque la modifica. Anche --yes lo fa ancora in questa release, con un avviso di deprecazione; dalla prossima release approverà solo i push che non distruggono nulla. Un push che si ferma lì elimina all’uscita il proprio database di appoggio <db>_dev_diff
  • Attiva la sicurezza a livello di riga su ogni tabella di collezione, tabelle di giunzione comprese, subito dopo la modifica dello schema e prima di qualsiasi passaggio che possa fallire: un push che si interrompe a metà lascia una nuova tabella che nega ogni richiesta invece che aperta a tutte
  • Applica le policy RLS delle tue collezioni e rimuove le policy sostituite da un push precedente
  • Lascia com’è ogni vincolo CHECK ed EXCLUDE sulla tabella di una collezione. Una collezione non può dichiarare né gli uni né gli altri, quindi sono regole proprie del database, scritte a mano o portate da un database che schema introspect ha adottato; per rimuoverne uno, eliminalo a mano (ALTER TABLE … DROP CONSTRAINT …). Anche i trigger e le funzioni che hai scritto non vengono toccati. Su 0.24 il primo push dopo un’introspezione eliminava ogni vincolo CHECK ed EXCLUDE senza chiedere conferma
  • Riconosce per nome esatto tutto ciò che lascia stare, compreso un ., [, ] o * al suo interno. Su 0.24 un indice scritto a mano con un nome come orders_idx[v1] veniva eliminato senza conferma, e un . nel nome di una tabella o di un indice faceva fallire ogni push con too many parts in pattern
  • Costruisce ogni indice di chiave esterna che Rebase deriva e che a una tabella esistente manca prima di pianificare, con CREATE INDEX CONCURRENTLY, così le scritture sulla tabella proseguono durante la build; Atlas lo trova poi già al suo posto. --dry-run li nomina e non costruisce nulla
  • Marca le policy che applica nello stesso modo dell’avvio del server, così il primo avvio dopo un push le lascia stare invece di ricreare ognuna
  • --dry-run elenca la row-level security che il push cambierebbe, sotto “Planned row-level security changes”: le tabelle su cui attiva la RLS, e ogni policy che crea, sostituisce o elimina, calcolate nello stesso modo in cui le decidono i passaggi del push stesso. Dice “No changes” solo quando non cambierebbero né lo schema né la row-level security. Dove la marcatura di una policy non corrisponde alla sua regola, o la policy non ne ha (una creata da db migrate, una scritta a mano), il dry run la ricrea dalla sua regola con un altro nome, in una transazione che annulla, e confronta ciò che Postgres ha memorizzato: una che risulta uguale non è una modifica. Così una regola che schema introspect ha riletto come SQL non pianifica nulla, e nemmeno le policy applicate da db migrate. Solo una che non ha potuto confrontare (per esempio, il lock della sua tabella non concesso entro due secondi) è elencata a parte. Il push stesso stampa una riga che conta le modifiche. Su 0.24 il dry run mostrava solo il piano di Atlas, che non contiene policy
  • Mostra una sola volta ogni istruzione che Atlas applica. Atlas si connette senza SSL a un database su questa macchina (localhost, 127.x, ::1) il cui URL non indica alcun sslmode, come fa l’app, invece di fallire con SSL is not enabled on the server; un URL che indica sslmode, o un altro host, viene passato così com’è scritto. Un’esecuzione di Atlas che fallisce prima di pianificare non lascia dietro di sé alcun database <db>_dev_diff
  • --dry-run non scrive nulla del progetto: src/schema.generated.ts resta com’è, e le copie drizzle/*.sql che una release precedente aveva committato vengono nominate, non eliminate. Solo l’input del piano va in .rebase/sql/, che non viene committato
  • Non crea file di migrazione

Quanto tempo richiede. Quasi tutto di un push è Atlas che calcola il piano: costruisce lo schema desiderato in un database scratch e lo confronta con quello reale, e quel costo cresce più in fretta del numero di tabelle — su un laptop tranquillo circa 0,25 s per piano con 11 tabelle, 0,9 s con 111 e 5,5 s con 411. Un push che non cambia nulla pianifica una volta; uno che cambia qualcosa pianifica due volte (il dry-run che legge il gate delle modifiche distruttive, poi l’applicazione). Quindi un push su uno schema di 400 o più tabelle richiede decine di secondi, e di più su una macchina occupata o lenta. Quel tempo è di Atlas, non del tuo database: i passaggi intorno — generare l’SQL, il lock RLS, la ricerca, i trigger, le policy, il ruolo — richiedono pochi secondi a quella dimensione, su una sola connessione al tuo database.

I file che genera lungo il percorso, tutti sotto .rebase/sql/ nella directory del backend. db push e db generate scrivono tutti e cinque a partire dalle tue collezioni a ogni esecuzione, prima di leggerne uno qualsiasi, quindi una copia committata non verrebbe letta da nulla. La directory ha il proprio .gitignore e non viene mai committata.

File Contiene
schema.sql Tabelle, colonne, vincoli e indici — lo stato desiderato di Atlas, e l’unico che confronta
policies.sql Le policy RLS in cui vengono compilate le tue securityRules
search.sql Le funzioni di ricerca full-text e le colonne generate, per le collezioni con un blocco search
vector.sql Le estensioni pgvector e gli indici ANN
triggers.sql rebase.set_updated_at() e i trigger BEFORE UPDATE dietro autoValue: "on_update"

Atlas gestisce il primo e nient’altro, quindi db push e l’allineamento dello schema all’avvio applicano da soli gli altri quattro. Un deployment solo con migrazioni — uno che esegue db migrate e mai db push — deve includere quei quattro in una migrazione a mano; db generate lo segnala quando una modifica è invisibile ad Atlas.

Se un progetto ha committato questi file in drizzle/ con una release precedente, quelle copie vengono eliminate alla prima esecuzione, e il comando le nomina una per una così puoi committare l’eliminazione. Elimina solo i file che iniziano con l’intestazione del generatore. Un file che hai scritto tu resta, così come drizzle/migrations/.

Genera file di migrazione SQL dalle modifiche allo schema:

rebase db generate

Cosa fa:

  • Confronta lo schema Drizzle con lo stato che costruiscono le migrazioni in drizzle/migrations/, non con il tuo database
  • Produce file di migrazione SQL con timestamp in backend/drizzle/migrations/
  • I file possono essere revisionati, modificati e committati nel controllo di versione
  • Nomina ogni istruzione della nuova migrazione che distrugge dati, usando il controllo che db push esegue prima di applicare: una tabella, colonna, schema, vista o tipo eliminati, un TRUNCATE, un cambio di tipo di colonna che può perdere valori, una modifica della chiave primaria, o l’eliminazione di un vincolo CHECK o EXCLUDE che il database contiene. Scrive comunque il file ed esce con 0, quindi avvisa e non rifiuta.
  • Termina con che cosa fare dopo: rebase db migrate e, per la prima migrazione di un progetto, il --baseline <version> di cui ha bisogno invece un database che l’avvio ha già preparato. Una migrazione successiva non riceve alcuna riga di baseline, poiché una distruttiva registrata con la baseline salterebbe il suo DROP, e un’esecuzione che non scrive alcuna migrazione dice solo questo.

Un CHECK aggiunto da una tua migrazione viene eliminato dalla successiva. db generate confronta i tuoi file di migrazione, non il database, e Atlas non ha modo di escludere un oggetto da quel confronto. Quindi un vincolo CHECK o EXCLUDE che hai scritto a mano in una migrazione, e che nessuna collezione può dichiarare, ritorna come ALTER TABLE … DROP CONSTRAINT … nella migrazione successiva che scrive, fuso nella stessa istruzione della modifica vera e propria. Il rapporto qui sopra nomina quell’eliminazione quando il database contiene già il vincolo; non la toglie dal file. Elimina la clausola prima di rebase db migrate, oppure lascia che rebase db migrate --refuse-destructive si fermi su di essa. db push legge il database e mantiene il vincolo.

Le migrazioni generate sono semplici file SQL — puoi ispezionarle e modificarle prima di applicarle.

Esegui tutte le migrazioni in sospeso:

rebase db migrate

Cosa fa:

  • Legge drizzle/migrations/ per le migrazioni non applicate
  • Le applica in ordine al database
  • Tiene traccia di quali migrazioni sono state applicate
  • Marca le policy di row-level security nello stesso modo di db push e dell’avvio del server, così il primo avvio dopo un migrate le lascia stare invece di ricreare ognuna. Una migrazione è stata scritta a partire dalle collezioni del suo tempo, quindi una policy viene marcata solo quando è esattamente ciò che la sua regola produce adesso: migrate la ricrea dalla sua regola con un altro nome, in una transazione che annulla, e confronta ciò che Postgres ha memorizzato, come fa db push --dry-run. Una policy che differisce non riceve alcuna marcatura, e il successivo avvio riapplica la sua tabella

--refuse-destructive la fa invece rifiutare. Prima esegue una prova a secco (dry run) delle migrazioni in sospeso. Se un’istruzione distrugge dati, con lo stesso controllo usato da db push e db generate, elenca quelle istruzioni ed esce con 1. Non applica nulla, nemmeno i file precedenti a quell’istruzione. Senza il flag, db migrate si comporta come prima e applica ogni file in sospeso:

rebase db migrate --refuse-destructive

Una migrazione che ricostruisce una colonna di ricerca la elimina prima, quindi questo flag rifiuta anche quella migrazione. Leggi l’elenco che stampa, poi esegui rebase db migrate senza il flag per applicarla.

Impostare una baseline su un database che Rebase ha già avviato

Sezione intitolata “Impostare una baseline su un database che Rebase ha già avviato”

Ogni avvio di Rebase assicura lo schema, e rebase db push lo applica direttamente. Un database su cui uno dei due sia mai stato eseguito ha quindi già le tabelle e i tipi che la prima migrazione creerebbe, e rebase db migrate si ferma su pq: type "posts_status" already exists (42710).

Nella migrazione non c’è nulla di sbagliato: il database è stato predisposto in un altro modo. Registrate dove si trova già, poi migrate normalmente:

rebase db migrate --baseline 20260906101530
rebase db migrate

La versione è il prefisso numerico del file di migrazione che descrive ciò che c’è nel database adesso. Quella migrazione e tutte le precedenti risultano applicate; tutto ciò che viene dopo viene eseguito. Su un database che non è mai stato avviato non serve alcuna baseline: migrate direttamente.

Lo stesso accade più avanti nel ciclo ordinario: modifica una collezione mentre rebase dev è in esecuzione, e l’avvio aggiunge la colonna prima che rebase db generate scriva la migrazione che la aggiunge, così rebase db migrate si ferma su column … already exists (42701). Imposta la baseline di quella migrazione allo stesso modo. --baseline funziona su un database che registra già delle migrazioni: segna come applicato tutto fino alla versione con atlas migrate set, e lo dice, oppure dice che non c’è nulla da registrare quando la cronologia la raggiunge già. Su 0.24 Atlas ignora --baseline una volta registrata una qualsiasi migrazione, quindi lo stesso errore ritorna. Una versione che non corrisponde ad alcun file in drizzle/migrations viene rifiutata prima di eseguire qualsiasi cosa, e il messaggio nomina le versioni presenti.

Branching del database per lo sviluppo parallelo:

rebase db branch create feature_auth
rebase db branch switch feature_auth
rebase db branch list
rebase db branch switch --off
rebase db branch info feature_auth
rebase db branch delete feature_auth

rebase db branch switch (e switch --off) e rebase db branch prune sono novità. create, list, info e delete sono stati rilasciati nella 0.17.

Ogni branch è una copia completa del database creata con CREATE DATABASE ... TEMPLATE, quindi richiede un vero server PostgreSQL e occupa lo stesso spazio su disco della sua origine.

Rileva la deriva a tre vie tra le tue definizioni di collezioni, lo schema Drizzle generato e il database PostgreSQL in esecuzione:

rebase doctor

Cosa controlla:

  • Collezioni ↔ Schema generato — sono sincronizzati?
  • Schema generato ↔ Database — ci sono modifiche non applicate?
  • Collezioni ↔ Database — c’è qualche deriva imprevista?

Esegui doctor ogni volta che qualcosa sembra non sincronizzato. Individua esattamente dove si trova la discrepanza.

Il confronto con il database include quello che l’avvio esegue prima di avvisare che schema.generated.ts e il database non concordano, eseguito tramite lo stesso codice. Così ogni differenza di cui l’avvio avvisa è anche nel rapporto, con le stesse parole: una colonna che è NOT NULL da una parte e nullable dall’altra, e una colonna che il database ha ancora e il file no (l’avvio non ne elimina mai una, quindi una proprietà rinominata lascia indietro la sua vecchia colonna). Ognuna è un avviso, e la correzione nomina rebase db push.

Il confronto con il database legge il database del progetto, risolto come lo risolve rebase db url — su uno scaffold appena creato quello gestito, che doctor avvia. Quando non se ne può risolvere nessuno, quella fase viene riportata come saltata anziché superata, e l’esecuzione non si chiude mai con “All schemas are in sync”: un controllo che non è avvenuto non è un certificato di buona salute.

Nomina anche ogni collezione priva di un blocco search la cui tabella contiene più di circa 10.000 righe. Una ricerca lì — la casella di ricerca dell’admin, .search(), ?searchString= — è un ILIKE '%term%' sulle colonne di testo, che nessun indice può servire, quindi ognuna legge l’intera tabella, e il suo conteggio la legge di nuovo (circa un terzo di secondo a 100.000 righe, in crescita con la tabella). Il numero di righe è la stima di Postgres stesso, quindi il controllo non scansiona nulla. È un suggerimento: non cambia il codice di uscita, e nulla viene indicizzato per te.

Nomina una giunzione orfana: una tabella public con la forma di una giunzione many-to-many che nessuna collezione dichiara. La forma è due colonne che insieme formano la chiave primaria, ciascuna una chiave esterna verso la tabella di una collezione. Rinominare o rimuovere una manyToMany lascia indietro la vecchia giunzione, perché push e avvio creano soltanto tabelle, e ogni riga di collegamento è ancora al suo interno. La nota è solo informativa, quindi non cambia il codice di uscita. La correzione è l’istruzione DROP TABLE, che doctor mostra e non esegue mai.

Genera un SDK tipizzato dalle tue definizioni di collezioni:

rebase generate-sdk

Cosa fa:

  • Legge ogni file di collezione in config/collections/ — i file che il backend serve, che siano elencati o no nel barrel index.ts — e si interrompe su uno che non si carica
  • Genera tipi TypeScript per tutte le entità in generated/sdk/
  • Produce un file database.types.ts da usare con createRebaseClient<Database>()

rebase dev lo esegue per te all’avvio e a ogni salvataggio sotto config/collections/. Eseguilo tu stesso in CI, in un repository che non ha collezioni (vedi --from più sotto), o ovunque rebase dev non sia in esecuzione.

Sulla 0.23 il comando legge solo i file elencati dal barrel index.ts, salta con un avviso quello che non si carica, e accetta la directory come --collections-dir; e rebase dev rigenera solo lo schema, quindi rigenerare i tipi dell’SDK spetta a te.

Opzioni:

Flag Descrizione
-c, --collections Directory delle collezioni (predefinito: config/collections/); i percorsi relativi vengono risolti dalla posizione in cui esegui il comando. Anche --collections-dir è accettato.
-o, --output Directory di output per l’SDK (predefinito: generated/sdk/)
--from <link|url> Legge lo schema da un progetto in esecuzione anziché dal codice locale. link usa il progetto collegato a questo checkout.
--token Token Bearer per l’endpoint del contratto (predefinito: $REBASE_SERVICE_KEY dalla shell; .env non viene letto)

--from è ciò che permette a un repository privo di collezioni — un frontend separato, una seconda web app, un’app mobile — di generare un client tipizzato dal progetto con cui parla. REBASE_SERVICE_KEY viene inviato solo al progetto collegato a questo checkout; per qualsiasi altro host, passa --token esplicitamente. La chiave in .env appartiene al tuo backend locale e non viene mai inviata. Un rifiuto dice quale di queste cose è successa: un --token rifiutato, una chiave della shell rifiutata, una chiave trattenuta per un host non collegato, una chiave presente solo in .env, oppure nessuna chiave. Su 0.24 dice sempre di passare --token o di impostare REBASE_SERVICE_KEY.

Utilizzo dopo la generazione:

import { createRebaseClient } from "@rebasepro/client";
import { collectionsDictionary, type Database } from "./generated/sdk/database.types";
const client = createRebaseClient<Database>({
baseUrl: import.meta.env.VITE_API_URL,
collections: collectionsDictionary,
});
// Full type safety and autocomplete
const { data } = await client.data.products.find();

I nomi dei campi nei tipi generati sono quelli che l’API serve. Il nome di un campo sul filo è la chiave della sua proprietà, e l’API è camelCase ovunque: l’introspezione genera una colonna created_at come proprietà createdAt con columnName: "created_at", e la serve come row.createdAt. La colonna in sé non viene toccata. L’accessor della collezione diventa un nome di proprietà allo stesso modo (my-notes → client.data.myNotes), ed è questo che collectionsDictionary rimappa sullo slug.

Il flusso di lavoro a iterazione rapida per lo sviluppo:

# 1. Edit your collection in config/collections/
# 2. Generate the Drizzle schema
rebase schema generate
# 3. Push directly to dev database
rebase db push

Il flusso di lavoro sicuro e revisionabile per la produzione:

# 1. Edit your collection in config/collections/
# 2. Generate the Drizzle schema
rebase schema generate
# 3. Generate SQL migration files
rebase db generate
# 4. Review the generated SQL in backend/drizzle/migrations/
# 5. Commit the migration to version control
git add backend/drizzle/migrations/
# 6. Apply in production
# A database Rebase has already booted needs a baseline the first time —
# see "Baselining a database Rebase has already booted" above.
rebase db migrate
Sintomo Soluzione
Could not detect an active database plugin Installa @rebasepro/server-postgres in backend/package.json
Il file di schema non si aggiorna Controlla che il percorso --collections punti alla directory corretta
La migrazione mostra modifiche impreviste Esegui rebase doctor per identificare la deriva
db push fallisce in produzione Usa db generate + db migrate invece
Una migrazione non deve eliminare dati in CI o in produzione rebase db migrate --refuse-destructive esce con 1 e non applica nulla quando un’istruzione in sospeso distrugge dati
db migrate fallisce con already exists (42710) L’avvio o db push hanno già predisposto lo schema — registratelo con rebase db migrate --baseline <version>
db migrate fallisce con column … already exists (42701) rebase dev ha aggiunto la colonna all’avvio — imposta la baseline di quella migrazione allo stesso modo
db push sul database gestito fallisce con Error: EOF Atlas ha perso una connessione che tiene il rebase dev in esecuzione. Riesegui; se continua a fallire, ferma prima rebase dev