Lesedauer 5 Minuten

PocketBase ist ein kompaktes Open-Source-Backend in einer einzigen Datei: Datenbank, Authentifizierung, Dateispeicher und Admin-Oberfläche inklusive. Richtig spannend wird es aber erst, wenn du eigene Logik einbaust. Genau dafür gibt es PocketBase JavaScript Hooks. Du legst einfach eine Datei in den Ordner pb_hooks, und PocketBase führt deinen Code bei bestimmten Ereignissen aus, ganz ohne eigenen Go-Build.

In diesem Beitrag bekommst du 10 praxisnahe Beispiele, die du direkt übernehmen und anpassen kannst. Alle Snippets basieren auf der aktuellen Hook-API ab PocketBase v0.23.

So funktionieren PocketBase Hooks

Jede Datei mit der Endung .pb.js im Ordner pb_hooks (neben der PocketBase-Binary) wird beim Start geladen. Änderungen erkennt PocketBase automatisch und startet neu. Innerhalb der Dateien registrierst du Handler wie onRecordCreate(), routerAdd() oder cronAdd().

Zwei Regeln solltest du dir merken:

  • Rufe e.next() auf. Hooks bilden eine Kette. Ohne e.next() wird die eigentliche Aktion (z. B. das Speichern) nicht ausgeführt.
  • Handler laufen isoliert. Variablen außerhalb eines Handlers sind darin nicht verfügbar. Gemeinsamen Code lädst du per require() innerhalb des Handlers (siehe Beispiel 10).

Die folgende Grafik zeigt, in welcher Reihenfolge die wichtigsten Record-Hooks bei einer Anfrage über die API greifen:

Ablauf der PocketBase Record-Hooks

Beispiel 1: Slug automatisch erzeugen

Du willst für Blogposts eine saubere URL, ohne dass jemand den Slug von Hand eintippen muss? Mit onRecordCreate setzt du das Feld vor dem Speichern, egal ob der Record über die API, das Dashboard oder einen anderen Hook entsteht.

Beispiel 2: Eigene Validierung

Die eingebauten Feldregeln decken viel ab, aber nicht alles. Mit onRecordCreateRequest prüfst du eingehende API-Daten und brichst mit einer verständlichen Fehlermeldung ab.

Beispiel 3: Besitzer automatisch setzen

Ein Klassiker: Der eingeloggte User soll automatisch als Autor eingetragen werden. So verhinderst du auch, dass jemand im Request einen fremden Autor mitschickt.

Beispiel 4: Willkommens-Mail nach der Registrierung

Sobald ein neuer User erfolgreich gespeichert wurde, verschickst du eine persönliche Begrüßung. Der Hook onRecordAfterCreateSuccess läuft erst, wenn der Datensatz wirklich in der Datenbank steht. Die SMTP-Einstellungen kommen aus dem Dashboard.

Beispiel 5: Eigene API-Route

Nicht jede Anfrage passt in das Standard-CRUD-Schema. Mit routerAdd() baust du eigene Endpunkte. Die Middleware $apis.requireAuth() sorgt dafür, dass nur eingeloggte User Zugriff haben.

Auch Pfadparameter sind möglich, zum Beispiel /api/hello/{name}. Den Wert liest du dann mit e.request.pathValue("name") aus.

Beispiel 6: Cronjob zum Aufräumen

Alte Sessions, abgelaufene Einladungen oder temporäre Uploads sammeln sich schnell an. Mit cronAdd() planst du wiederkehrende Aufgaben direkt in PocketBase, ganz ohne externen Scheduler.

Vielleicht fragst du dich, ob der Cronjob bei jedem Start oder jeder Änderung an der Datei erneut hinzugefügt wird und am Ende mehrfach läuft. Die Antwort ist nein, und zwar aus zwei Gründen:

  • Die ID ist eindeutig: Der erste Parameter von cronAdd() (hier "cleanup_invites") identifiziert den Job. Existiert bereits ein Job mit dieser ID, ersetzt PocketBase ihn, statt einen zweiten anzulegen.
  • Hooks werden nur beim Start geladen: Die Dateien in pb_hooks laufen einmal beim Start der App. Änderst du eine Datei, startet PocketBase den Prozess automatisch neu, und der Scheduler beginnt wieder mit einer leeren Liste.

Wichtig ist also nur, dass jeder Job eine eigene ID bekommt. Verwendest du dieselbe ID in zwei Dateien, überschreibt der zuletzt geladene Job den anderen. Brauchst du einen Job nicht mehr, entfernst du ihn mit cronRemove("cleanup_invites"). Alle registrierten Jobs siehst du im Dashboard unter Settings > Crons. Mehr dazu findest du in der offiziellen Doku zum Jobs Scheduling.

Beispiel 7: Webhook an einen externen Dienst

Wenn ein Beitrag veröffentlicht wird, soll zum Beispiel ein Slack-Channel oder ein Static-Site-Build benachrichtigt werden. Dafür gibt es $http.send(). Packe den Aufruf in ein try/catch, damit ein ausgefallener Fremddienst nicht deine App blockiert.

Beispiel 8: Felder in der Antwort ausblenden

Manche Felder brauchst du intern, sie sollen aber nie beim Client landen. Mit onRecordEnrich passt du jede Record-Antwort an, egal ob sie aus einer Liste, einer Einzelabfrage oder einem Realtime-Event stammt.

Beispiel 9: Audit-Log für Änderungen

Wer hat wann den Status einer Bestellung geändert? Ein einfacher Audit-Log beantwortet das. Lege dafür eine Collection audit_logs mit den Feldern collection, record_id, old_status und new_status an. Im Model-Hook onRecordUpdate hast du über original() noch Zugriff auf den alten Wert.

Weil hier e.app statt $app verwendet wird, läuft das Speichern des Logs in derselben Transaktion wie die eigentliche Änderung, falls eine aktiv ist.

Beispiel 10: Gemeinsamen Code auslagern

Wenn mehrere Hooks dieselbe Hilfsfunktion brauchen, lagerst du sie in ein Modul aus. Wichtig: Die Datei darf nicht auf .pb.js enden, sonst lädt PocketBase sie als Hook. Und der require()-Aufruf gehört in den Handler, weil Handler isoliert laufen.

Tipps für den Alltag mit PocketBase Hooks

  • Typen nutzen: PocketBase legt beim Start die Datei pb_data/types.d.ts an. Verweise am Anfang deiner Hook-Datei mit /// <reference path="../pb_data/types.d.ts" /> darauf und dein Editor liefert Autovervollständigung.
  • Loggen statt raten: Mit $app.logger().info() landen deine Meldungen in den Logs im Dashboard.
  • Request oder Model? Request-Hooks (...Request) greifen nur bei API-Aufrufen und kennen den eingeloggten User. Model-Hooks wie onRecordCreate greifen bei jedem Speichern, auch aus Cronjobs oder dem Dashboard.
  • Kein Node.js: Die Hooks laufen in der eingebetteten JS-Engine goja. npm-Pakete mit Node-APIs wie fs funktionieren dort nicht.

Alle verfügbaren Hooks und Hilfsfunktionen findest du in der offiziellen PocketBase-Dokumentation zur JavaScript-Erweiterung.

FAQ zu PocketBase JavaScript Hooks

Wo lege ich meine Hook-Dateien ab?

Im Ordner pb_hooks direkt neben der PocketBase-Binary. Jede Datei muss auf .pb.js enden, damit sie geladen wird.

Muss ich PocketBase nach einer Änderung neu starten?

Nein. PocketBase überwacht den Ordner pb_hooks und startet bei Änderungen automatisch neu. Unter Windows kann das je nach Setup eingeschränkt sein.

Warum wird mein Record nicht gespeichert?

Meist fehlt der Aufruf von e.next(). Ohne ihn bricht die Hook-Kette ab und die eigentliche Aktion findet nicht statt.

Kann ich npm-Pakete in Hooks verwenden?

Nur eingeschränkt. Reine JavaScript-Bibliotheken ohne Node-spezifische APIs kannst du per require() laden. Pakete, die auf fs, http oder Ähnliches angewiesen sind, laufen nicht.

Sind JavaScript Hooks langsamer als Go-Erweiterungen?

Ja, etwas. Für typische Aufgaben wie Validierung, Mails oder Webhooks fällt das kaum ins Gewicht. Bei sehr rechenintensiver Logik lohnt sich eine Erweiterung in Go.