Aller au contenu

SDK typé — Prise en main

Le package @rebasepro/client fournit un SDK JavaScript type-safe pour interagir avec votre backend Rebase. Il prend en charge :

  • Opérations sur les données — CRUD avec filtrage, tri et pagination
  • Récupération des relations — Incluez les entités associées avec .include()
  • Abonnements en temps réel — Mises à jour en direct basées sur WebSocket
  • Synchronisation hors-ligne & local-first — Base de données locale de lignes activable à la demande, écritures instantanées hors-ligne, requêtes en direct
  • Authentification — Gestion des tokens, connexion, inscription, OAuth
  • Stockage — Téléversement, téléchargement et gestion de fichiers
  • Fonctions personnalisées — Appel de points de terminaison serveur personnalisés
pnpm add @rebasepro/client

rebase dev dérive un port libre à partir du chemin du projet plutôt que d’utiliser un port fixe, donc lisez baseUrl à partir de l’URL affichée — aucun port n’est partagé par tous les projets. Dans un frontend Vite, il s’agit de la variable VITE_API_URL que l’échafaudage écrit dans .env ; dans un script, d’une variable d’environnement qui vous est propre.

import { createRebaseClient } from "@rebasepro/client";
const client = createRebaseClient({
baseUrl: import.meta.env.VITE_API_URL,
});

L’websocketUrl est dérivée automatiquement de baseUrl (http → ws, https → wss). Vous pouvez la remplacer explicitement si nécessaire :

const client = createRebaseClient({
baseUrl: import.meta.env.VITE_API_URL,
websocketUrl: import.meta.env.VITE_WS_URL,
});
Option Type Description
baseUrl string URL du backend. Lisez-la depuis ce que rebase dev a affiché, ou depuis votre déploiement
websocketUrl string URL WebSocket — dérivée automatiquement de baseUrl si omise
token string Token JWT statique pour les appels de serveur à serveur
apiPath string Préfixe de l’API (par défaut : "/api")
fetch typeof fetch Implémentation fetch personnalisée (ex. pour le SSR)
onUnauthorized () => Promise<boolean> Gestionnaire personnalisé pour les erreurs 401 — retournez true pour réessayer
realtime boolean Ouvre la WebSocket (par défaut true) — définissez sur false dans les scripts ponctuels
collections Record<string, string> Associe les noms d’accesseurs aux slugs des collections
offline boolean | OfflineConfig Synchronisation local-first — désactivée par défaut
impersonate string Exécute chaque requête en tant que cet utilisateur (par uid), pour vérifier ce qu’un utilisateur voit. Uniquement avec la session d’un administrateur : l’API de données, les fonctions et le temps réel s’exécutent en son nom, toute autre route refuse

Générez un client entièrement typé à partir des définitions de vos collections :

rebase generate-sdk

Pendant que rebase dev tourne, vous n’en avez pas besoin : il régénère les types dans generated/sdk/ au démarrage et à chaque enregistrement sous config/collections/. Lancez vous-même la commande en CI, dans un dépôt frontend sans collections (rebase generate-sdk --from link), ou partout où rebase dev ne tourne pas. Sur la 0.23, rebase dev laisse les types du SDK inchangés, exécutez donc la commande après chaque modification de collection.

Passez ensuite le paramètre de type Database à createRebaseClient pour bénéficier d’une autocomplétion complète :

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 types
const { data } = await client.data.products.find();

Lorsque Database est fourni, createRebaseClient renvoie une instance de CreateRebaseClientResult<DB>. Cela associe directement les accesseurs de collection en camelCase sur client.data à leurs types correspondants, vous offrant une autocomplétion complète sur les opérations et les types de collections (ex. client.data.products.find()).

collectionsDictionary associe chaque accesseur au slug utilisé sur le réseau. Passez-le chaque fois qu’un slug n’est pas déjà un nom de propriété valide — my-notes n’est accessible sous la forme client.data.myNotes que parce que le dictionnaire l’indique.

Quelques noms sur client.data sont pris : collection est la fonction dynamique collection(slug), et then, toJSON et $$typeof sont laissés tranquilles pour les vérifications de promesse et de sérialisation. Une collection dont l’accesseur serait l’un d’eux est générée sous la forme <name>Collection — le slug collection devient client.data.collectionCollection — et generate-sdk affiche un avertissement qui nomme les deux. client.data.collection("collection") l’atteint aussi.

Le nom d’un champ sur le réseau est sa clé de propriété, et l’API utilise le camelCase d’un bout à l’autre. Une propriété createdAt stockée dans une colonne created_at est row.createdAt, et la clé étrangère d’une relation est authorId même si la colonne reste author_id. where et orderBy reposent sur le même type Row, donc ce qui compile correspond exactement à ce à quoi le backend répond.

Une clé de propriété que vous avez écrite est votre clé, quelle que soit sa forme — rien ne renomme un nom que vous avez choisi. Les deux clés qui sont dérivées plutôt que déclarées — la clé étrangère d’une relation et une colonne récupérée par introspection — sont en camelCase.

Row décrit une lecture, Insert un create() et Update un update() — ils n’ont pas la même structure. Une colonne qui n’est pas obligatoire est T | null sur les trois, ce qui permet à une écriture de la vider ; la clé primaire est toujours présente lors d’une lecture et ne peut jamais être définie lors d’une mise à jour ; une cible belongsTo peut être écrite soit sous forme de relation ({ author: 5 }), soit sous forme de sa clé étrangère ({ authorId: 5 }) ; et une appartenance hasMany ou manyToMany s’écrit comme une liste d’identifiants cibles ou d’objets { id } ({ tags: ["t-1"] }), chacun portant un _pivot lorsque la table de jonction déclare des colonnes de charge utile. Un champ que personne n’a le droit d’écrire (access: { write: [] }) ne figure ni sur Insert ni sur Update. Une colonne date se lit sous forme de chaîne ISO, et une écriture accepte soit cette chaîne, soit un Date.

// Create
const product = await client.data.products.create({
name: "Camera",
price: 299,
});
// Query with filters
const { data } = await client.data.products
.where("price", ">=", 100)
.orderBy("createdAt", "desc")
.limit(10)
.find();
// Real-time subscription
const unsubscribe = client.data.products.listen(
{ where: { active: ["==", true] } },
(response) => console.log("Updated:", response.data)
);

Dans un frontend Rebase, le client est créé une seule fois et partagé via le contexte :

import { createRebaseClient } from "@rebasepro/client";
const client = createRebaseClient({ baseUrl: API_URL });
<Rebase client={client} ...>

Accédez-y depuis n’importe quel composant :

import { useRebaseClient } from "@rebasepro/app";
function MyComponent() {
const client = useRebaseClient();
// client.data, client.auth, client.storage, client.functions
}