PocketBun Differences From PocketBase
This page tracks user-relevant differences between PocketBase and PocketBun.
Notation used in examples:
erefers to the hook or route event parameter (for example:onBootstrap((e) => { ... })orrouterAdd("GET", "/path", (e) => { ... })).
Quick links:
- PocketBase To PocketBun Migration Checklist
- Runtime And Distribution
- CLI Defaults And Paths
- Hooks Plugin Naming
- Hooks API And Module Loading
- Async API Extensions
- Operational Differences
- PocketBase Docs Topics That Do Not Apply Directly
- Intentional Omissions
PocketBase To PocketBun Migration Checklist
Use this as a quick migration recipe for an existing PocketBase project.
- Switch executable and update flow.
- Replace
pocketbasecommands withpocketbun. - There is no PocketBase-style binary self-update command; update via package manager.
- Replace
- Create (or convert to) a Bun project.
- Option A: convert your existing project directory:
- initialize package metadata:
bun init - add PocketBun dependency:
bun add pocketbun
- initialize package metadata:
- Option B: scaffold a new PocketBun project and copy your existing data/code:
- create project:
bun create pocketbun my-app - copy your PocketBase
pb_*directories into the new project (pb_data,pb_hooks,pb_migrations, optionalpb_public)
- create project:
- Option A: convert your existing project directory:
- Keep the project layout, but verify startup working directory.
- Keep
pb_data,pb_hooks,pb_migrations, and optionalpb_public. - PocketBun resolves default paths from current working directory (CWD), so start from your project root or pass explicit dirs.
- Keep
- Move hooks as-is, then fix hook-chain calls.
- In handlers that call
e.next(), return/await it:- sync:
return e.next() - async:
const err = await e.next(); if (err) return err; ...
- sync:
- This is especially important for
onBootstrapwhen using async startup paths. - If you see
OnBootstrap hook didn't fail but the app is still not bootstrapped, this is usually the cause. - In
.pb.tshooks, use standardimportfor neighboring files and dependencies when needed.
- In handlers that call
- Keep API clients and route assumptions.
- Existing client SDK usage should continue to work with the same API base paths (
/api/,/_/).
- Existing client SDK usage should continue to work with the same API base paths (
- If you embed PocketBun programmatically, prefer JSVM-compatible naming.
- Prefer
RegisterJSVM*/MustRegisterJSVM*for naming parity with PocketBase JSVM docs. RegisterHooksPlugin*/MustRegisterHooksPlugin*remain aliases.
- Prefer
- Run a migration smoke test before deploying.
- Start:
pocketbun serve --dev - Verify health:
GET /api/health - Verify custom hooks/routes and auth flows you use in production.
- Start:
- Review the sections below for details.
- Use this checklist for the quick pass, then check each section in this page only where your app uses that feature.
Runtime And Distribution
PocketBase:
- distributed as a Go binary
- updated via binary release flow
PocketBun:
- distributed as npm/Bun package
- updated via package manager (
bun update pocketbun) - CLI binary name is
pocketbun
CLI Defaults And Paths
PocketBase defaults are resolved relative to executable location. PocketBun resolves defaults from current working directory.
Default paths in PocketBun CLI:
./pb_data./pb_hooksand./pb_migrations(derived frompb_dataunless explicitly set)./pb_public(unless explicitly set)
This prevents accidental writes into node_modules-adjacent paths when used as package dependency.
Hooks Plugin Naming
PocketBase JS extension naming uses jsvm package naming.
PocketBun keeps those names as primary and also provides aliases:
- preferred for parity:
RegisterJSVM*,MustRegisterJSVM* - aliases:
RegisterHooksPlugin*,MustRegisterHooksPlugin*
Both names map to the same plugin registration behavior.
Hooks API And Module Loading
PocketBun supports JSVM-style lowercase naming and keeps Go-style aliases where applicable:
- preferred hook object names:
bindFunc,bind,unbind,length,trigger - alias hook object names:
BindFunc,Bind,Unbind,Length,Trigger - app method style: prefer
$app.onServe();$app.OnServe()is also accepted pb_hooksglobal hook bindings intentionally mirror upstream JSVM (so there is no globalonServe(...))
For pb_hooks module loading:
.pb.tssupports ESM imports from local files and dependencies (node_modules).pb.jssupportsrequire(...)
For code-first BaseApp usage:
- built-in route middlewares are available as package exports (for example
RequireGuestOnly,SkipSuccessActivityLog) - you can bind them directly in
OnServeroutes withe.Router.GET(...).Bind(...)
Async API Extensions
PocketBun keeps sync-compatible APIs but adds async alternatives for I/O-heavy paths.
| Area | PocketBase-compatible sync API | PocketBun async extension |
|---|---|---|
| Archive helpers | Create, Extract |
CreateAsync, ExtractAsync |
| App bootstrap/serve | app.bootstrap(), serve(...) |
app.bootstrapAsync(), serveAsync(...) |
| Migration helper | migrate(...) |
migrateAsync(...) |
| Hooks plugin register | RegisterJSVM(...) |
RegisterJSVMAsync(...) |
| Filesystem factories | NewFilesystem() |
NewFilesystemAsync() |
| JSVM helpers | $http.send(...), $os.readFile(...) |
$http.sendAsync(...), $os.readFileAsync(...) |
Operational Differences
Activity logs
PocketBun persists activity logs through a background worker to reduce main-thread blocking.
Thumbnails
PocketBun uses Sharp for image resizing. Output bytes may differ from PocketBase Go image stack.
- BMP thumbnails are emitted as PNG (Sharp limitation).
Templates
PocketBun $template helper supports common PocketBase template patterns.
For closer Go text/template parity, install optional go-text-template.
JSVM RequestEvent request/response surface
For custom routes, e below means the route event parameter passed to routerAdd(..., (e) => { ... }).
PocketBun now supports the common PocketBase custom-route access patterns:
e.response.header().set(...)e.request.pathValue(...)ande.request.setPathValue(...)e.request.url.pathe.request.url.query().get(...)e.request.header.get(...)
Remaining incompatibilities in this area:
- Go
http.Requestform helpers are not implemented one.request(formFile,parseForm,parseMultipartForm,formValue,postFormValue).- use
e.findUploadedFiles(...),e.bindBody(...), ore.requestInfo().bodyinstead.
- use
- Go
http.ResponseWriterwrite primitives are not implemented (e.response.write(...),e.response.writeHeader(...)).- use route event helpers (
e.json,e.string,e.html,e.xml,e.blob,e.noContent,e.redirect).
- use route event helpers (
SQL placeholders and dbx rewriting
PocketBun supports dbx-style query marker rewriting for SQLite helpers. Logged placeholder formats can differ while query behavior remains compatible.
Dev SQL logging format
In --dev mode, PocketBun prints SQL logs using a Bun-native format based on
the executed rewritten SQL ([X.XXms] <sql>). The exact formatting may differ
from PocketBase and is informational only.
Windows behavior
HooksWatchrestart behavior has no effect on Windows.- filesystem/process timing can differ from Unix-like systems.
PocketBase Docs Topics That Do Not Apply Directly
These upstream topics are either intentionally excluded or need reinterpretation for PocketBun:
- all
go-*extension docs pages (PocketBun is JS/TS extension-first) - binary self-update workflow for PocketBase executable
- operational assumptions tied to standalone Go binary path semantics
- some upstream docs response examples may use slightly different sample keys than runtime output (for example health sample
statusvs runtimecode)
These are not bugs in PocketBun docs; they are product-level differences.
Intentional Omissions
Intentionally not provided in PocketBun:
- PocketBase binary self-update command/plugin workflow
- Go extension workflow as first-class user path
Deferred until demand:
- Dart SDK-specific docs