Typisierte serverseitige Daten
Der Browser-Client wird typisiert, indem Sie Ihre generierte Database an
createRebaseClient<Database>() übergeben (siehe Typisierte SDK-Generierung).
Die serverseitigen Datenoberflächen haben keinen solchen Aufruf, dem Sie sie übergeben könnten:
context.datain einem Collection-Callback,rebase.dataAsAdminamrebase-Singleton,- das
rebase, das eine Custom Function oder ein Cron-Job erhält.
Sie sagen TypeScript also einmal für das ganze Projekt, welche Database sie bedienen.
Bis dahin bleiben sie untypisiert: jede Collection unter jedem Namen, Zeilen als
Record<string, unknown>. Für ein Projekt, das nichts registriert, ändert sich nichts.
Die Database registrieren
Abschnitt betitelt „Die Database registrieren“Seit 0.25
rebase generate-sdk --registerNeben dem SDK in generated/sdk/ schreibt dies zwei Dateien nach
config/generated/: eine Kopie von database.types.ts und eine register.ts, die
die Registrierung deklariert:
import type { Database } from "./database.types";
declare module "@rebasepro/types" { interface Register { database: Database; }}Sie liegen in config/, weil Ihre Collection-Dateien zweimal kompiliert werden, vom
Backend (dessen tsconfig.json ../config/ einschließt) und vom eigenen tsc des
config-Pakets, und config/ das eine Verzeichnis ist, das beide lesen. Eine
Registrierung dort, die ../generated/sdk/ importierte, würde eine Datei von
außerhalb von config/ in die Kompilierung dieses Pakets ziehen, was TypeScript ablehnt. Eine
Registrierung typisiert also Ihre Funktionen, Ihre Cron-Jobs und die Callbacks in Ihren
Collection-Dateien, in beiden Kompilierungen. Sie ist nur eine Deklaration: Die Runtime tut
genau das, was sie zuvor tat.
Das Flag wird in rebase.json gemerkt, als "registerDatabase": true an der
Backend-App, sodass jedes spätere rebase generate-sdk und rebase dev bei jeder
Collection-Änderung beide Dateien aktuell hält. Das gilt auch bei einem frischen Klon und in
CI: Die .gitignore des Scaffolds ignoriert generated/-Verzeichnisse, und das
erste rebase generate-sdk oder rebase dev schreibt sie erneut. Ein Projekt ohne
rebase.json registriert weiter, solange config/generated/register.ts
vorhanden ist. Um zu untypisierten serverseitigen Daten zurückzukehren, entfernen Sie registerDatabase und
löschen Sie config/generated/.
Sie können die Registrierung auch selbst schreiben, in jeder Datei, die das Backend kompiliert
(backend/src/rebase-env.d.ts, mit Import von ../../generated/sdk/database.types).
Sie erreicht dann nur die Kompilierung des Backends: Im eigenen tsc des config-Pakets
bleibt context.data das untypisierte Objekt, sodass Callbacks in beiden Fällen kompilieren müssen.
Was typisiert wird
Abschnitt betitelt „Was typisiert wird“// config/collections/orders.ts — a callbackcallbacks: { async beforeSave({ context, values }) { const { data } = await context.data.customers.find({ where: { email: ["==", "ada@example.com"] }, // `email` is a column of customers }); data[0]?.email; // completes from the customers Row
await context.data.customer.find(); // error: no collection `customer` return values; }}// backend/functions/stats.tsimport { defineFunction } from "@rebasepro/server/functions";
export default defineFunction((app, { rebase }) => { app.get("/", async (c) => { const { data } = await rebase.dataAsAdmin.orders.find({ where: { status: ["==", "paid"] }, // `status` and its values are checked }); return c.json({ paid: data.length }); });});// backend/crons/cleanup.tsimport { defineCron } from "@rebasepro/server";
export default defineCron({ name: "Cleanup", schedule: "0 3 * * *", async handler({ rebase }) { // `create` takes the collection's Insert type: required columns are required. await rebase.dataAsAdmin.auditLog.create({ action: "cleanup" }); },});Jeder Accessor ist so typisiert wie der des Browser-Clients: find, findById
und listen geben das Row der Collection zurück; where und orderBy sind
nach ihren Spalten benannt; include nennt ihre Relationen; create nimmt ihr
Insert und update ihr Update. Accessoren sind die camelCase-Namen aus der
generierten Datei (order_items ist orderItems).
Der Server löst diese Namen über die registrierten Collections auf, sodass eine
Collection my-notes, die in einer Tabelle notes gespeichert ist, context.data.myNotes ist.
collection(slug) mit einem Slug, den die Database deklariert, ist genauso typisiert:
context.data.collection("my-notes") ist context.data.myNotes. Ein Slug, den Sie zur
Laufzeit berechnen, oder einer, den das Schema nicht deklariert, erhält den
untypisierten Client.
Eine Funktion, ohne die Registrierung
Abschnitt betitelt „Eine Funktion, ohne die Registrierung“defineFunction und defineCron nehmen den Typ auch als Argument, das Vorrang vor
der Registrierung hat. Nutzen Sie es, um eine einzelne Datei zu typisieren oder um typisierte Daten auszuprobieren, bevor Sie
registrieren:
import type { Database } from "../../generated/sdk/database.types";
export default defineFunction<Database>((app, { rebase }) => { // rebase.dataAsAdmin is typed from Database here});Helfer, die das Datenobjekt annehmen
Abschnitt betitelt „Helfer, die das Datenobjekt annehmen“Ein Helfer, der context.data oder rebase.dataAsAdmin erhält, nennt den
registrierten Typ, sodass er typisiert bleibt und beides akzeptiert:
import type { RebaseSdkData, RegisteredDatabase } from "@rebasepro/types";
export async function paidOrders(data: RebaseSdkData<RegisteredDatabase>) { return data.orders.find({ where: { status: ["==", "paid"] } });}Ein Helfer, der mit einem bloßen RebaseSdkData deklariert ist, behält den untypisierten Typ, und ein
typisiertes Datenobjekt ist ihm nicht zuweisbar. Ändern Sie den Parameter wie oben, wenn
Sie registrieren.