JavaScript engine
The prebuilt PocketBase v0.17+ executable comes with embedded ES5 JavaScript engine (goja) which enables you to write custom server-side code using plain JavaScript.
You can start by creating *.pb.js
file(s) inside a pb_hooks
directory.
routerAdd("GET", "/hello/:name", (c) => {
let name = c.pathParam("name")
return c.json(200, { "message": "Hello " + name })
})
onModelAfterUpdate((e) => {
console.log("user updated...", e.model.get("email"))
}, "users")
For convenience, when making changes to the files inside pb_hooks
, the process will
automatically restart/reload itself (currently supported only on UNIX based platforms). The
*.pb.js
files are loaded per their filename sort order.
The running PocketBase instance is exposed as global $app
object (
for all exposed APIs, please refer to the
JS Types reference
).
For most parts, the JavaScript APIs are derived from Go with 2 main differences:
- Go exported method and field names are converted to camelCase, for example:
app.Dao().FindRecordById("example", "RECOR_ID")
becomes
$app.dao().findRecordById("example", "RECOR_ID")
. - Errors are thrown as regular JavaScript exceptions and not returned as Go values.
TypeScript declarations and code completion
While you can’t use directly TypeScript (without transpiling it to JS on your own), PocketBase
comes with builtin ambient TypeScript declarations that can help providing information
and documentation about the available global variables, methods and arguments, code completion, etc. as
long as your editor has TypeScript LSP support
(most editors either have it builtin or available as plugin).
The types declarations are stored in
pb_data/types.d.ts
file. You can point to those declarations using the
reference tripple-slash directive
at the top of your JS file:
onAfterBootstrap((e) => {
console.log("App initialized!")
})
If after referencing the types your editor still doesn’t perform linting, then you can try to rename your
file to have .pb.ts
extension.
Caveats and limitations
Handlers scope
Each handler function (hook, route, middleware, etc.) is
serialized and executed in its own isolated context as a separate “program”. This means
that you don’t have access to custom variables and functions declared outside of the handler scope. For
example, the below code will fail:
const name = "test"
onAfterBootstrap((e) => {
console.log(name)
})
The above serialization and isolation context is also the reason why error stack trace line numbers may
not b