JavaScript event hooks
Select PocketBase JavaScript event hooks and implement handlers that preserve lifecycle and error behavior.
Use JavaScript event hooks when you need to observe or change PocketBase behavior at application, request, record, mail, collection, or realtime boundaries. Every handler receives an event object and must call e.next() to continue the hook chain; throwing an error or omitting that call stops the chain.
Before you begin
Create a pb_hooks/*.pb.js file and understand whether your handler runs before persistence, after a database statement, or after a transaction commits. Record model hooks can run outside an HTTP request and therefore do not have request context.
Choose a hook family
Application hooks
Use onBootstrap after application resources are initialized; call e.next() before accessing the database. Use onBootstrapClear while resources are being cleared; database access after e.next() is unavailable. onSettingsReload observes settings replacement, and onTerminate runs during termination but may not complete during an abrupt stop. Backup lifecycle hooks include onBackupCreate and onBackupRestore.
onBootstrap((e) => { e.next(); console.log("Application ready") })
onSettingsReload((e) => { e.next(); console.log("Settings reloaded") })Mailer hooks
Use onMailerSend to intercept a message from $app.newMailClient(). The record-specific hooks—onMailerRecordAuthAlertSend, onMailerRecordPasswordResetSend, onMailerRecordVerificationSend, onMailerRecordEmailChangeSend, and onMailerRecordOTPSend—also expose the related record and metadata. Change the message before continuing.
onMailerSend((e) => {
e.message.subject = `[PocketBase] ${e.message.subject}`
e.next()
})Realtime hooks
Use onRealtimeConnectRequest when a client establishes an SSE connection, onRealtimeSubscribeRequest when subscriptions change, and onRealtimeMessageSend before an SSE message is sent. Work after e.next() in a connect handler occurs after the client disconnects.
Record and collection hooks
Record hooks include onRecordEnrich, onRecordValidate, create, update, and delete hooks, their *Execute variants, and their After*Success and After*Error variants. Pass collection names after the handler to scope a hook, for example onRecordValidate(handler, "posts").
onRecordValidate((e) => {
if (e.record.get("title") == "") throw new BadRequestError("Title is required")
e.next()
}, "posts")
onRecordAfterCreateSuccess((e) => {
console.log("Committed record", e.record.id)
e.next()
}, "posts")Use onRecordAfterCreateSuccess, onRecordAfterUpdateSuccess, and onRecordAfterDeleteSuccess when you need the committed persistence outcome. A successful earlier model hook does not guarantee that its surrounding transaction commits. The corresponding error hooks report failed persistence; errors can be immediate or delayed when a transaction rolls back.
Record model hooks can be triggered by console commands, scheduled jobs, or direct application calls, so they do not have request context. Use the designated Record *Request hooks when you need request headers, body, query parameters, or authentication state.
Implement a safe handler
Choose a pre-validation hook for rejecting input, an *Execute hook for work immediately around the database statement, or an After*Success hook for committed persistence.
Pass one or more collection names when the behavior applies only to selected collections. Keep global handlers narrow because they can run for unrelated records.
Make the decision, then call e.next() exactly when the chain should continue. Throw a documented API error when the operation must stop.
Trigger the lifecycle event with a safe test record or request. Confirm the expected log, response, message, or committed record state, and test the failure path separately.
onRecordCreate or onRecordUpdate success means the transaction committed. Use the corresponding After*Success hook for post-commit work.Troubleshooting
The handler never reaches later hooks
Confirm that every successful branch calls e.next(). A thrown error or missing call stops the chain by design.
Database access fails in onBootstrap
Move database-dependent work after e.next(). During onBootstrapClear, do the opposite: access resources before e.next().
Request data is unavailable
Check whether the hook is a record model hook. If it can run outside HTTP, use the appropriate Record *Request hook for request context.
Next steps
See JavaScript database and filesystem for persistence and file operations, or JavaScript routes and console commands for custom entry points.