Zum Inhalt springen

Benutzerdefinierte Funktionen

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

Erstellen Sie eine Datei in Ihrem backend/functions/-Verzeichnis, die standardmäßig eine Hono-App exportiert:

// backend/functions/hello.ts
import { 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.

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:

  1. Das Verzeichnis nach .ts / .js-Dateien durchsuchen
  2. Überprüfen, ob jeder Standard-Export eine Hono-App ist (duck-typed über .fetch() + .routes)
  3. Jede App unter /api/functions/<filename> einbinden
  4. Die Authentifizierungs-Middleware anwenden (siehe Authentifizierung unten)
DateiEinbindungspfad
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

Der Loader akzeptiert zwei Exportformate:

import { Hono } from "hono";
const app = new Hono();
app.get("/status", (c) => c.json({ ok: true }));
export default app;
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.

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

Verwenden Sie die integrierten Auth-Helfer von Rebase:

import { Hono } from "hono";
const app = new Hono();
// Public endpoint — no auth required
app.get("/public", (c) => {
return c.json({ message: "Anyone can access this" });
});
// Protected endpoint — requires a valid JWT
app.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 endpoint
app.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.

Benutzerdefinierte Funktionen laufen neben Rebase, sodass Sie über zwei Ansätze auf die Datenbank zugreifen können:

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.ts
import { 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;
// backend/functions/reports.ts
import { Hono } from "hono";
import { db } from "../src/db"; // Your Drizzle instance
import { 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;

Benutzerdefinierte Funktionen werden nachdem initializeRebaseBackend() die Kernkonfiguration abgeschlossen hat, geladen und eingebunden. Die Initialisierungsreihenfolge ist:

  1. Bootstrappers – Datenbankverbindungen, Auth-Tabellen, Echtzeitdienste
  2. Auth-Routen/api/auth/*, /api/admin/*
  3. Speicherrouten/api/storage/*
  4. Datenrouten/api/data/* (CRUD für Collections)
  5. Benutzerdefinierte Funktionen/api/functions/*
  6. Cron-Jobs/api/cron/*
  7. 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 routes
app.get("/health", (c) => c.json({ status: "ok" }));
// Rebase initialization — registers all /api/* routes
const instance = await initializeRebaseBackend({ app, /* ... */ });
// backend/functions/stripe-webhook.ts
import { 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;

Wenn eine Funktion erfolgreich geladen wird, sehen Sie:

⚡ Loaded function route: hello

Wenn 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.