Docs/Reference
Hooks
Every hook of a GoLocomotive app, its event fields and its default action, plus routes, middlewares and the request event.
A hook is app.<hook>(handler, ...collections). No collection means every collection. Handlers run in the order they were bound, and each one must await e.next() for the default action to run. A handler that skips next() stops the chain and answers with e.json(...) itself. How to choose between them: Server: hooks, routes, crons.
Record request hooks#
They fire on an HTTP request only, never on a write made by a hook or a script. They run BEFORE the rule check of the write.
| hook | event, beside e.next() | default action |
|---|
onRecordsListRequest | request, collection, result | answer result |
onRecordViewRequest | request, collection, record | answer record |
onRecordCreateRequest | request, collection, data (mutable), record (set by next) | the rule, the write, the answer |
onRecordUpdateRequest | request, collection, original (stored, rule not checked yet), data, record | the rule, the write, the answer |
onRecordDeleteRequest | request, collection, record (stored) | the rule, the delete, 204 |
After success hooks#
They fire after the commit, for EVERY write: an HTTP request, a hook, a script, a cascade. A throw is logged and the write stays.
| hook | event |
|---|
onRecordAfterCreateSuccess | collection, record |
onRecordAfterUpdateSuccess | collection, record, original |
onRecordAfterDeleteSuccess | collection, record |
Auth hooks#
| hook | event | default action |
|---|
onRecordAuthRequest | request, collection, record, token, authMethod (password, otp, or '' on a refresh) | the second factor, the alert, then answer { token, record } |
onRecordAuthWithPasswordRequest | request, collection, identity, password, record (null when unknown) | check the password, then auth |
onRecordAuthWithOTPRequest | request, collection, record, otpId | use the code, then auth |
onRecordRequestOTPRequest | request, collection, email, record | make and mail the code |
onRecordAuthRefreshRequest | request, collection, record | answer a fresh token |
onRecordRequestPasswordResetRequest | request, collection, record | mail the link |
onRecordConfirmPasswordResetRequest | request, collection, record | set the password |
onRecordRequestEmailChangeRequest | request, collection, record, newEmail | mail the link to the new address |
onRecordConfirmEmailChangeRequest | request, collection, record, newEmail | set email and verified |
Mail and backup hooks#
| hook | event | default action |
|---|
onMailerSend | message (mutable); kind and record on auth mails | send |
onBackupCreate | name (mutable, '' for the default name), exclude (mutable: entries of the data folder to leave out) | write the zip into the store |
onBackupRestore | name, exclude (mutable: entries to keep in place) | stage, stop, swap, restart |
app.db is always in a backup. backups and the temp folder are always out. Code after next() in onBackupRestore runs only when the restore failed before it stopped the app: on success the process restarts.
The request event#
| member | what |
|---|
request | the Request |
url | the parsed URL |
params | path parameters |
auth | the signed in record, or null |
isSuperuser | true for a _superusers record |
realIp | the client IP, read from trustedProxy headers, else the socket's |
remoteIp | the socket's address, always |
body() | the JSON or multipart body, parsed once |
json(status, data) | answer JSON |
noContent() | answer 204 |
response | what was answered, after next() |
Routes and middlewares#
app.route('POST', '/api/orders/:id/close', async (e) => {
const order = await app.records.getOne('orders', e.params.id ?? '')
if (order.owner !== e.auth?.id) throw new ApiError(403, 'Not your order.')
await app.records.update('orders', order.id, { status: 'done' })
return e.noContent()
}, requireAuth('users'))
app.route(method, path, handler, ...middlewares). Paths take :name and a * wildcard.requireAuth(...collections): 401 without a signed in record of those collections (any auth collection when none is named).requireSuperuser(): 403 for anyone but a superuser.bodyLimit(bytes): this route's body limit; 0 is none; a function of the app and the path parameters is read per request.- A middleware is a handler that calls
e.next(). A handler can also return a Response. ApiError(status, message, data?) thrown anywhere becomes PocketBase's error shape.
Crons#
| call | what |
|---|
app.cron(name, spec, fn) | five fields (* , - /) or @yearly @monthly @weekly @daily @hourly, UTC, checked each minute. The same name replaces. A throw is logged, the schedule goes on |
__loco_cleanup | built in, hourly: expired codes and two factor sessions, idle rate limit clients |
__loco_backup | built in when backups.cron is set |
This page as markdown