PocketBase — API Surface#

API types#

REST/HTTP API, CLI (Cobra), Library (Go embedding API), Plugin/Extension system (hook-based + JavaScript JSVM)


REST/HTTP API#

Router#

  • Router: Custom tools/router — a thin wrapper around Go 1.22 stdlib net/http with {param} and {path...} pattern matching, extended via tools/hook for per-route middleware chains.
  • Route registration: Explicit functional binding in apis/base.go:NewRouter(). Each API domain has a dedicated bind*Api(app, apiGroup) function in its own file.

Middleware chain (global, applied to every route)#

Priority-ordered hook.Handler[*core.RequestEvent] instances registered via pbRouter.Bind(...):

PriorityIDPurpose
lowest (−40 rel.)pbActivityLoggerRecords request info to aux SQLite log DB
−30 rel.pbPanicRecoverRecovers panics, returns 500
(CORS)CORS headers (configured per ServeConfig.AllowedOrigins)
−20 rel.pbLoadAuthTokenParses Authorization: <TOKEN> header, sets e.Auth on RequestEvent
−10 rel.pbSecurityHeadersAdds X-XSS-Protection, X-Content-Type-Options, X-Frame-Options
defaultpbRateLimitToken-bucket rate limiting (rules stored in settings)
defaultpbBodyLimitBody size cap (default DefaultMaxBodySize)
−99999pbWWWRedirectwww→non-www redirect for configured domains (registered in Serve())

Per-route auth middleware (applied to individual route groups)#

  • RequireAuth(...collections) — valid JWT from any (or named) auth collection
  • RequireSuperuserAuth() — alias for RequireAuth("_superusers")
  • RequireSuperuserOrOwnerAuth(ownerIdPathParam) — superuser or matching record owner
  • RequireSameCollectionContextAuth(collectionPathParam) — auth collection must match path param
  • RequireGuestOnly() — rejects authenticated requests
  • BodyLimit(n) — per-route body size override (used for backup upload)
  • SkipSuccessActivityLog() — suppresses activity logging for successful calls

Authentication#

JWT tokens (tools/security). Tokens carry type (auth, file, otp, etc.). The loadAuthToken global middleware auto-populates e.Auth from the Authorization header. Routes then gate access with the per-route auth middlewares above.

Key endpoints#

All routes are under the /api/ prefix.

Records — /api/collections/{collection}/records#

MethodPathAuthDescription
GET/api/collections/{collection}/recordsaccess rulesList records with filtering/sorting/pagination
GET/api/collections/{collection}/records/{id}access rulesView single record
POST/api/collections/{collection}/recordsaccess rulesCreate record (multipart or JSON)
PATCH/api/collections/{collection}/records/{id}access rulesUpdate record
DELETE/api/collections/{collection}/records/{id}access rulesDelete record

Record Auth — /api/collections/{collection}/#

MethodPathAuthDescription
GET/api/collections/{collection}/auth-methodsguestList enabled auth methods
POST/api/collections/{collection}/auth-refreshRequireAuthRefresh auth token
POST/api/collections/{collection}/auth-with-passwordguestPassword authentication
POST/api/collections/{collection}/auth-with-oauth2OAuth2 authentication
POST/api/collections/{collection}/request-otpRequest OTP code
POST/api/collections/{collection}/auth-with-otpOTP authentication
POST/api/collections/{collection}/request-password-resetRequest password reset email
POST/api/collections/{collection}/confirm-password-resetConfirm password reset
POST/api/collections/{collection}/request-verificationRequest email verification
POST/api/collections/{collection}/confirm-verificationConfirm email verification
POST/api/collections/{collection}/request-email-changeRequireAuthRequest email change
POST/api/collections/{collection}/confirm-email-changeConfirm email change
POST/api/collections/{collection}/impersonate/{id}RequireSuperuserAuthCreate auth token for another record
GET/POST/api/oauth2-redirectOAuth2 redirect handler (PKCE callback)

Collections — /api/collections (all require superuser)#

MethodPathDescription
GET/api/collectionsList collections
POST/api/collectionsCreate collection
GET/api/collections/{collection}View collection
PATCH/api/collections/{collection}Update collection
DELETE/api/collections/{collection}Delete collection
DELETE/api/collections/{collection}/truncateDelete all records in collection
PUT/api/collections/importBulk import collection schemas
GET/api/collections/meta/scaffoldsList collection type scaffolds

Settings — /api/settings (all require superuser)#

MethodPathDescription
GET/api/settingsList all settings
PATCH/api/settingsUpdate settings
POST/api/settings/test/s3Test S3 connectivity
POST/api/settings/test/emailSend test email
POST/api/settings/apple/generate-client-secretGenerate Apple OAuth2 client secret

Logs — /api/logs (superuser only)#

MethodPathDescription
GET/api/logsList request logs
GET/api/logs/statsRequest log statistics
GET/api/logs/{id}View single log entry

Backups — /api/backups (superuser only)#

MethodPathDescription
GET/api/backupsList backup files
POST/api/backupsCreate backup
POST/api/backups/uploadUpload backup (no body limit)
GET/api/backups/{key}Download backup (via file token)
DELETE/api/backups/{key}Delete backup
POST/api/backups/{key}/restoreRestore from backup

Realtime — /api/realtime#

MethodPathDescription
GET/api/realtimeSSE connect (long-poll, no activity log)
POST/api/realtimeUpdate SSE subscriptions

Files — /api/files#

MethodPathDescription
POST/api/files/tokenGenerate short-lived file access token
GET/api/files/{collection}/{recordId}/{filename}Download/serve file

Crons — /api/crons (superuser only)#

MethodPathDescription
GET/api/cronsList registered cron jobs
POST/api/crons/{id}Manually trigger a cron job

Batch — /api/batch#

MethodPathDescription
POST/api/batchExecute a transaction of multiple record operations (create/update/upsert/delete)

Health — /api/health#

MethodPathDescription
GET/api/healthHealth check endpoint

Admin UI — /_/{path...}#

Serves the embedded Svelte Admin SPA from ui/dist/ (Go embed, go:embed), with Cache-Control and CSP headers. Gzip-compressed.


CLI#

  • Framework: Cobra (github.com/spf13/cobra)
  • Binary entry point: examples/base/main.go (the reference production binary)

Command structure#

pocketbase
├── serve [domain(s)]        Start the HTTP/HTTPS server
│   --http     <addr>        HTTP listen address (default 127.0.0.1:8090)
│   --https    <addr>        HTTPS listen address (enables autocert TLS)
│   --origins  <origins>     CORS allowed origins (default *)
│
├── superuser                Manage superuser accounts
│   ├── upsert <email> <pw>  Create or update superuser
│   ├── create <email> <pw>  Create new superuser
│   ├── update <email> <pw>  Change superuser password
│   ├── delete <email>       Delete superuser
│   └── otp    <email>       Generate OTP for superuser
│
├── migrate                  (plugin: migratecmd)
│   Apply pending DB migrations; generate migration stubs
│
└── update                   (plugin: ghupdate)
    Self-update binary from GitHub releases

Flag patterns#

  • Global persistent flags on root command: --dir (data directory), --dev (dev mode), --encryptionEnv (settings AES key env var), --queryTimeout.
  • Each Cobra plugin (migratecmd, ghupdate, jsvm) registers additional PersistentFlags on RootCmd when Register() is called.
  • No environment variable binding in PocketBase itself; the --encryptionEnv flag takes an env var name, not the value directly.

Plugin / Extension system#

Mechanism#

Hook-based, using the generic tools/hook.Hook[T Resolver]. All extension points are lifecycle hooks on core.App. The same hook API is used by:

  1. Go extensions — library users call app.OnXxx().BindFunc(...) before app.Start().
  2. JavaScript extensionsplugins/jsvm registers Go-side hook handlers that invoke JS callbacks from files in pb_hooks/.

Extension points (hook categories)#

CategoryExample hooks
LifecycleOnBootstrap, OnServe, OnTerminate
Record CRUDOnRecordCreate, OnRecordUpdate, OnRecordDelete, OnRecordValidate, OnRecordCreateRequest, OnRecordAfterCreateSuccess, …
Model CRUDOnModelCreate, OnModelUpdate, OnModelDelete, OnModelAfterCreateSuccess, …
CollectionOnCollectionCreate, OnCollectionUpdate, OnCollectionDelete, …
AuthOnRecordAuthRequest, OnRecordAuthWithPassword, OnRecordAuthWithOAuth2, OnRecordAuthWithOTP, …
MailerOnMailerSend, OnMailerRecordPasswordResetSend, …
SettingsOnSettingsReload, OnSettingsUpdate
RealtimeOnRealtimeConnect, OnRealtimeSubscribe, OnRealtimeMessageSend
BackupOnBackupCreate, OnBackupRestore
RequestOnServe — fires before HTTP listener starts; plugins attach routes here via e.Router

OnServe — the primary route extension point#

app.OnServe().BindFunc(func(e *core.ServeEvent) error {
    e.Router.GET("/custom/{name}", func(re *core.RequestEvent) error {
        return re.JSON(200, map[string]string{"hello": re.Request.PathValue("name")})
    })
    return e.Next()
})

ServeEvent.Router is the same *router.Router[*core.RequestEvent] instance used for all built-in routes, so custom routes get the full middleware stack automatically.

JavaScript extension entry points (plugins/jsvm)#

  • Files matching *.pb.js or *.pb.ts in pb_hooks/ are loaded and executed.
  • The JS runtime exposes the full Go hook API: onRecordCreateRequest(fn), onServe(fn), routerAdd("GET", "/path", handler), etc.
  • TypeScript type definitions are pre-generated in plugins/jsvm/internal/types/ for IDE autocomplete.
  • A pool of pre-warmed goja.Runtime instances avoids per-request cold starts.

Standard middleware wrappers (library API)#

Two convenience functions allow embedding standard Go HTTP middleware into the PocketBase hook chain:

  • apis.WrapStdHandler(h http.Handler) func(*core.RequestEvent) error
  • apis.WrapStdMiddleware(m func(http.Handler) http.Handler) func(*core.RequestEvent) error

Library API#

PocketBase is designed to be used as a Go library. Users write a main.go (~20 lines) that:

  1. Creates a pocketbase.New() instance (which embeds core.App)
  2. Registers plugins and hooks
  3. Calls app.Start()

Public packages exported for library use#

PackagePurpose
github.com/pocketbase/pocketbaseTop-level PocketBase struct; entry point for library users
github.com/pocketbase/pocketbase/coreApp interface, BaseApp, all model types (Record, Collection, etc.), RequestEvent
github.com/pocketbase/pocketbase/apisNewRouter, Serve, ServeConfig, all middleware constructors, Static, WrapStdHandler
github.com/pocketbase/pocketbase/cmdNewServeCommand, NewSuperuserCommand — Cobra command constructors
github.com/pocketbase/pocketbase/tools/hookHook[T], Handler[T] — the extension primitive
github.com/pocketbase/pocketbase/tools/routerRouter[T], RouterGroup[T], ApiError helpers
github.com/pocketbase/pocketbase/plugins/jsvmRegister(app, Config) — JSVM plugin
github.com/pocketbase/pocketbase/plugins/migratecmdRegister(app, rootCmd, Config) — migration CLI plugin
github.com/pocketbase/pocketbase/plugins/ghupdateRegister(app, rootCmd, Config) — self-update plugin
github.com/pocketbase/pocketbase/formsHigh-level form validators (used internally; usable from library code)
github.com/pocketbase/pocketbase/mailsEmail sending helpers
github.com/pocketbase/pocketbase/migrationsBuilt-in system migrations (auto-registered)

API style#

  • Primarily interface + concrete struct (core.App / core.BaseApp).
  • Hooks use a generic middleware chain — callers BindFunc(func(e *T) error) or Bind(&hook.Handler[*T]{...}).
  • Configuration for plugins is via config structs (jsvm.Config, apis.ServeConfig, etc.).
  • No functional options; no builder pattern. Explicit struct fields throughout.

Backward compatibility#

No explicit versioning strategy in code. The project signals stability via semver git tags. The core.App interface is very large (~150 methods) — additions are backward-compatible for callers, but implementations (e.g. test stubs) must be updated. The godoc comment on core.App explicitly notes it is not intended to be implemented by third parties, which avoids the ISP problem in practice.