Benutzerdefinierte Funktionen
Übersicht
Abschnitt betitelt „Übersicht“Benutzerdefinierte Funktionen ermöglichen es Ihnen, beliebige Hono API-Routen neben den automatisch generierten CRUD-Endpunkten von Rebase hinzuzufügen. Sie folgen dem gleichen dateibasierten Erkennungsmuster wie Collections und Cron-Jobs: Legen Sie eine TypeScript-Datei in Ihr functions/-Verzeichnis, und Rebase bindet sie automatisch ein.
Verwenden Sie benutzerdefinierte Funktionen für:
- Endpunkte für Geschäftslogik – Genehmigungen, Aktionen, benutzerdefinierte Workflows
- Integrationen von Drittanbietern – Stripe-Webhooks, Slack-Befehle, externe API-Proxys
- Öffentliche Endpunkte – Kontaktformulare, Lead-Erfassung, Health Checks
- Aggregierte Abfragen – Dashboard-Statistiken, Berichte, Analysen
Definieren einer benutzerdefinierten Funktion
Abschnitt betitelt „Definieren einer benutzerdefinierten Funktion“Erstellen Sie eine Datei in Ihrem backend/functions/-Verzeichnis, die standardmäßig eine Hono-App exportiert:
// backend/functions/hello.tsimport { Hono } from "hono";
const app = new Hono();
app.get("/", (c) => { return c.json({ message: "Hello from custom function!" });});
export default app;Dies wird unter /api/functions/hello eingebunden. Der Dateiname (ohne Erweiterung) wird zum Routenpräfix.
Konfiguration
Abschnitt betitelt „Konfiguration“Aktivieren Sie benutzerdefinierte Funktionen, indem Sie functionsDir zu Ihrer Backend-Konfiguration hinzufügen:
import path from "path";
const instance = await initializeRebaseBackend({ // ... other config functionsDir: path.resolve(__dirname, "../functions"),});Rebase wird:
- Das Verzeichnis nach
.ts/.js-Dateien durchsuchen - Überprüfen, ob jeder Standard-Export eine Hono-App ist (duck-typed über
.fetch()+.routes) - Jede App unter
/api/functions/<filename>einbinden - Die Authentifizierungs-Middleware anwenden (siehe Authentifizierung unten)
Dateibenennung und Routen-Mapping
Abschnitt betitelt „Dateibenennung und Routen-Mapping“| Datei | Einbindungspfad |
|---|---|
functions/hello.ts | /api/functions/hello/* |
functions/send-invoice.ts | /api/functions/send-invoice/* |
functions/webhooks.ts | /api/functions/webhooks/* |
Dateien, die übersprungen werden:
index.ts/index.js— reserviert*.test.ts/*.test.js— Testdateien*.d.ts— Typdeklarationen
Exportformate
Abschnitt betitelt „Exportformate“Der Loader akzeptiert zwei Exportformate:
Hono App (empfohlen)
Abschnitt betitelt „Hono App (empfohlen)“import { Hono } from "hono";const app = new Hono();app.get("/status", (c) => c.json({ ok: true }));export default app;Factory-Funktion
Abschnitt betitelt „Factory-Funktion“import { Hono } from "hono";export default function () { const app = new Hono(); app.get("/status", (c) => c.json({ ok: true })); return app;}Beide werden über Duck-Typing erkannt – der Loader prüft auf die Eigenschaften .fetch() und .routes, sodass jede Hono-kompatible Instanz funktioniert, unabhängig von der installierten Hono-Version.
Authentifizierung
Abschnitt betitelt „Authentifizierung“Benutzerdefinierte Funktionen werden mit der gleichen Authentifizierungs-Middleware wie die Datenrouten eingebunden, jedoch mit requireAuth: false. Das bedeutet:
- Das JWT des Benutzers wird, falls vorhanden, geparst und in den Kontext injiziert
- Anfragen werden aber nicht abgelehnt, wenn kein JWT bereitgestellt wird
- Sie müssen Routen, die Authentifizierung benötigen, explizit schützen
Routen schützen
Abschnitt betitelt „Routen schützen“Verwenden Sie die integrierten Auth-Helfer von Rebase:
import { Hono } from "hono";
const app = new Hono();
// Public endpoint — no auth requiredapp.get("/public", (c) => { return c.json({ message: "Anyone can access this" });});
// Protected endpoint — requires a valid JWTapp.post("/protected", async (c) => { const user = c.get("user"); // Injected by Rebase middleware if (!user) { return c.json({ error: "Unauthorized" }, 401); } return c.json({ message: `Hello, ${user.uid}` });});
// Admin-only endpointapp.post("/admin-only", async (c) => { const user = c.get("user"); const roles: string[] = user?.roles ?? []; if (!roles.includes("admin")) { return c.json({ error: "Admin access required" }, 403); } return c.json({ message: "Admin operation succeeded" });});
export default app;Die JWT-Middleware von Rebase ist auf die integrierten API-Routen (/api/data, /api/auth, etc.) beschränkt. Benutzerdefinierte Funktionsrouten erhalten den geparsten Benutzerkontext, aber Sie müssen die Zugriffskontrolle selbst durchsetzen.
Auf die Datenbank zugreifen
Abschnitt betitelt „Auf die Datenbank zugreifen“Benutzerdefinierte Funktionen laufen neben Rebase, sodass Sie über zwei Ansätze auf die Datenbank zugreifen können:
1. Über das Rebase Singleton (Empfohlen)
Abschnitt betitelt „1. Über das Rebase Singleton (Empfohlen)“Das Paket @rebasepro/server-core stellt ein rebase-Singleton bereit, das Ihnen von überall in Ihrem Backend Administratorzugriff auf alle app-spezifischen Dienste (Daten, Authentifizierung, Speicher, E-Mail) ermöglicht.
// backend/functions/approve-job.tsimport { Hono } from "hono";import { rebase } from "@rebasepro/server-core";
const app = new Hono();
app.post("/:id/approve", async (c) => { const id = c.req.param("id");
// Use the admin-level data API (bypasses RLS) await rebase.data.saveEntity("jobs", { id, status: "published", approved_at: new Date().toISOString(), });
return c.json({ success: true });});
export default app;2. Über direkten Drizzle-Zugriff
Abschnitt betitelt „2. Über direkten Drizzle-Zugriff“// backend/functions/reports.tsimport { Hono } from "hono";import { db } from "../src/db"; // Your Drizzle instanceimport { sql } from "drizzle-orm";
const app = new Hono();
app.get("/stats", async (c) => { const result = await db.execute(sql` SELECT COUNT(*) as total FROM jobs WHERE status = 'published' `); return c.json({ totalJobs: result.rows[0]?.total });});
export default app;Reihenfolge der Routenregistrierung
Abschnitt betitelt „Reihenfolge der Routenregistrierung“Benutzerdefinierte Funktionen werden nachdem initializeRebaseBackend() die Kernkonfiguration abgeschlossen hat, geladen und eingebunden. Die Initialisierungsreihenfolge ist:
- Bootstrappers – Datenbankverbindungen, Auth-Tabellen, Echtzeitdienste
- Auth-Routen –
/api/auth/*,/api/admin/* - Speicherrouten –
/api/storage/* - Datenrouten –
/api/data/*(CRUD für Collections) - Benutzerdefinierte Funktionen ←
/api/functions/* - Cron-Jobs –
/api/cron/* - WebSocket – Echtzeit-Abonnements
Das bedeutet, dass Ihre benutzerdefinierten Funktionen Zugriff auf alle initialisierten Dienste haben. Registrieren Sie alle Routen, die vor Rebase ausgeführt werden müssen, direkt in der Hono-App, bevor Sie initializeRebaseBackend() aufrufen:
const app = new Hono();
// This runs BEFORE Rebase routesapp.get("/health", (c) => c.json({ status: "ok" }));
// Rebase initialization — registers all /api/* routesconst instance = await initializeRebaseBackend({ app, /* ... */ });Beispiel: Webhook-Handler
Abschnitt betitelt „Beispiel: Webhook-Handler“// backend/functions/stripe-webhook.tsimport { Hono } from "hono";import Stripe from "stripe";import { instance } from "../src/index";
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!);const app = new Hono();
app.post("/", async (c) => { const sig = c.req.header("stripe-signature")!; const body = await c.req.text();
const event = stripe.webhooks.constructEvent( body, sig, process.env.STRIPE_WEBHOOK_SECRET! );
if (event.type === "checkout.session.completed") { const session = event.data.object; await instance.driver.data.subscriptions.create({ user_id: session.client_reference_id, stripe_id: session.subscription, status: "active", }); }
return c.json({ received: true });});
export default app;Fehlerbehebung
Abschnitt betitelt „Fehlerbehebung“Wenn eine Funktion erfolgreich geladen wird, sehen Sie:
⚡ Loaded function route: helloWenn das Laden fehlschlägt, liefert der Loader eine Diagnoseausgabe:
[functions] broken-function.ts: default export is not a Hono app or factory. Skipping. export type: object (SomeClass) prototype methods: constructor, someMethod Hint: ensure the function exports a Hono app created with the same hono version as the server.Nächste Schritte
Abschnitt betitelt „Nächste Schritte“- Backend-Übersicht – Vollständige Referenz zur Backend-Konfiguration
- Entitäts-Callbacks – Logik bei Datenänderungen ausführen
- Cron-Jobs – Geplante Hintergrundaufgaben