SDK tipizzato — Primi passi
Panoramica
Sezione intitolata “Panoramica”Il pacchetto @rebasepro/client fornisce un SDK JavaScript type-safe per interagire con il tuo backend Rebase. Gestisce:
- Operazioni sui dati — CRUD con filtri, ordinamento e paginazione
- Recupero delle relazioni — Include entità correlate con
.include() - Sottoscrizioni in tempo reale — Aggiornamenti live basati su WebSocket
- Sincronizzazione offline e local-first — Database locale opzionale per le righe, scritture offline istantanee, query live
- Autenticazione — Gestione dei token, login, registrazione, OAuth
- Storage — Caricamento, download e gestione dei file
- Funzioni personalizzate — Chiamata a endpoint server personalizzati
Installazione
Sezione intitolata “Installazione”pnpm add @rebasepro/clientCreazione di un client
Sezione intitolata “Creazione di un client”rebase dev ricava una porta libera dal percorso del progetto anziché usarne una fissa, quindi leggi baseUrl dall’URL stampato a video — non esiste una porta comune a tutti i progetti. In un frontend Vite, questa corrisponde a VITE_API_URL che lo scaffold scrive in .env; in uno script, puoi usare una tua variabile d’ambiente.
import { createRebaseClient } from "@rebasepro/client";
const client = createRebaseClient({ baseUrl: import.meta.env.VITE_API_URL,});websocketUrl viene ricavato automaticamente da baseUrl (http → ws, https → wss). Puoi sovrascriverlo esplicitamente se necessario:
const client = createRebaseClient({ baseUrl: import.meta.env.VITE_API_URL, websocketUrl: import.meta.env.VITE_WS_URL,});Opzioni di configurazione
Sezione intitolata “Opzioni di configurazione”| Opzione | Tipo | Descrizione |
|---|---|---|
baseUrl |
string |
URL del backend. Leggilo dall’output di rebase dev o dal tuo deployment |
websocketUrl |
string |
URL del WebSocket — ricavato automaticamente da baseUrl se omesso |
token |
string |
Token JWT statico per chiamate server-to-server |
apiPath |
string |
Prefisso delle API (predefinito: "/api") |
fetch |
typeof fetch |
Implementazione fetch personalizzata (es. per SSR) |
onUnauthorized |
() => Promise<boolean> |
Gestore personalizzato per l’errore 401 — restituisci true per riprovare |
realtime |
boolean |
Apre il WebSocket (predefinito true) — imposta su false in script one-shot |
collections |
Record<string, string> |
Mappa i nomi degli accessor agli slug delle collection |
offline |
boolean | OfflineConfig |
Sincronizzazione local-first — disattivata per impostazione predefinita |
impersonate |
string |
Esegue ogni richiesta come questo utente (per uid), per verificare cosa vede un utente. Solo con la sessione di un amministratore: API dati, funzioni e realtime vengono eseguiti come quell’utente, ogni altra route rifiuta |
Generazione dell’SDK tipizzato
Sezione intitolata “Generazione dell’SDK tipizzato”Genera un client completamente tipizzato a partire dalle definizioni delle tue collection:
rebase generate-sdkMentre rebase dev è in esecuzione non serve: rigenera i tipi in generated/sdk/ all’avvio e a ogni salvataggio sotto config/collections/. Esegui tu stesso il comando in CI, in un repository frontend che non ha collection (rebase generate-sdk --from link), o ovunque rebase dev non sia in esecuzione.
Sulla 0.23, rebase dev lascia i tipi dell’SDK intatti, quindi esegui il comando dopo
ogni modifica a una collection.
Quindi passa il parametro di tipo Database a createRebaseClient per ottenere l’autocompletamento completo:
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 autocomplete on collection names and field typesconst { data } = await client.data.products.find();Quando viene fornito Database, createRebaseClient restituisce un’istanza di CreateRebaseClientResult<DB>. Questo mappa gli accessor delle collection in camelCase direttamente su client.data con i relativi tipi, fornendo l’autocompletamento completo su operazioni e tipi delle collection (es. client.data.products.find()).
collectionsDictionary mappa ciascun accessor sullo slug utilizzato a livello di rete (wire). Passalo ogni volta che uno slug non è già un nome di proprietà valido — my-notes è accessibile come client.data.myNotes solo perché il dizionario lo definisce.
Alcuni nomi su client.data sono riservati: collection è la funzione dinamica collection(slug), mentre then, toJSON e $$typeof vengono lasciati stare per i controlli di promise e serializzazione. Una collezione il cui accessor sarebbe uno di questi viene generata come <name>Collection — lo slug collection diventa client.data.collectionCollection — e generate-sdk stampa un avviso che nomina entrambi. Anche client.data.collection("collection") la raggiunge.
Nomi dei campi
Sezione intitolata “Nomi dei campi”Il nome di un campo sulla rete è la sua chiave di proprietà, e l’API utilizza ovunque il camelCase. Una proprietà createdAt memorizzata in una colonna created_at corrisponde a row.createdAt, e la foreign key di una relazione è authorId anche se la colonna rimane author_id. where e orderBy sono basati sullo stesso tipo Row, quindi ciò che compila è ciò a cui risponde il backend.
Una chiave di proprietà scritta da te rimane invariata, qualunque sia la sua forma — nulla rinomina un nome da te scelto. Le due chiavi che vengono derivate anziché dichiarate, ovvero la foreign key di una relazione e una colonna letta tramite introspezione, sono in camelCase.
Row descrive una lettura, Insert una create() e Update una update() — non hanno la stessa forma. Una colonna non obbligatoria è T | null su tutte e tre, così una scrittura può azzerarla; la primary key è sempre presente in lettura e non è mai impostabile in un aggiornamento; il target di un belongsTo può essere scritto sia come relazione ({ author: 5 }) sia come foreign key ({ authorId: 5 }); e un’appartenenza hasMany o manyToMany si scrive come un elenco di id target o di oggetti { id } ({ tags: ["t-1"] }), ciascuno con un _pivot quando la tabella di giunzione dichiara colonne di payload. Un campo che nessuno può scrivere (access: { write: [] }) non compare né su Insert né su Update. Una colonna date si legge come stringa ISO, e una scrittura accetta quella stringa oppure un Date.
Esempio rapido
Sezione intitolata “Esempio rapido”// Createconst product = await client.data.products.create({ name: "Camera", price: 299,});
// Query with filtersconst { data } = await client.data.products .where("price", ">=", 100) .orderBy("createdAt", "desc") .limit(10) .find();
// Real-time subscriptionconst unsubscribe = client.data.products.listen( { where: { active: ["==", true] } }, (response) => console.log("Updated:", response.data));Utilizzo con React
Sezione intitolata “Utilizzo con React”In un frontend Rebase, il client viene creato una sola volta e condiviso tramite context:
import { createRebaseClient } from "@rebasepro/client";
const client = createRebaseClient({ baseUrl: API_URL });
<Rebase client={client} ...>Accedivi da qualsiasi componente:
import { useRebaseClient } from "@rebasepro/app";
function MyComponent() { const client = useRebaseClient(); // client.data, client.auth, client.storage, client.functions}Passaggi successivi
Sezione intitolata “Passaggi successivi”- Interrogazione dei dati — CRUD, filtri, paginazione e relazioni
- Autenticazione — Accesso, registrazione, OAuth, sessioni
- Sottoscrizioni in tempo reale — Dati in tempo reale con WebSocket
- Sincronizzazione offline e local-first — Lavora senza connessione e sincronizza al suo ripristino
- Storage e file — Carica, scarica e gestisci i file