Go server extensions
A package that needs to run Go code on the server - IMAP/SMTP, custom HTTP endpoints, long-lived workers, anything that doesn’t fit in PocketBase’s JS hooks - ships a server/ subdirectory with its own Go module. The generator wires it into the app shell’s go.mod and into the generated server/package_extensions.go entry point. For the contract the package itself implements (the Register(app) function, the module layout, testing), see Server. This page covers what the app shell does around that.
When to use Go
Most packages don’t need a Go server. If you can express your logic as:
- A PocketBase JS hook → put a
.pb.jsfile inpb-hooks/. - A migration → put a file in
pb-migrations/. - Client-only code → don’t ship server code at all.
Reach for Go only when you need:
- A long-lived network server (IMAP, SMTP, WebRTC signalling).
- Streaming or binary HTTP endpoints that JS hooks can’t serve cleanly.
- Logic that must run outside any PocketBase record event (cron-style tickers, worker pools).
- Native integrations the PocketBase JS runtime doesn’t expose.
Declaring the module
Two fields in the manifest:
server: { package: 'server', module: 'tinycld.org/packages/example' },
package is the subdirectory name, by convention 'server'. module is the Go module path declared in that subdirectory’s go.mod - use the tinycld.org/packages/<slug> namespace to keep module paths out of collisions.
What the generator writes
On each npm run packages:generate, for every package with a server field, the generator:
-
Appends a
require <module> v0.0.0line to the app shell’sserver/go.mod, inside a tagged block:module tinycld.org/app go 1.25.0 // --- package extensions (auto-generated, do not edit) --- require tinycld.org/packages/example v0.0.0 // --- end package extensions --- -
Appends a matching
replacedirective pointing at the sibling repo’s real path:// --- package extensions (auto-generated, do not edit) --- replace tinycld.org/packages/example => ../../example/server // --- end package extensions ---The replace path is relative to
tinycld/server/and points directly at the sibling repo’sserver/subdirectory at~/code/tinycld/<slug>/server/. (Bundled@tinycld/coreis the exception - it lives inside the app shell and is referenced viareplace tinycld.org/core => ../packages/@tinycld/core/server.) -
Regenerates
server/package_extensions.go, a small Go file whoseinit()calls each package’sRegister(app)in manifest order. The app shell’smain.gois in the same package and invokes it so every linked package gets a chance to wire in hooks, endpoints, and workers beforeapp.Start().
The // --- package extensions (auto-generated, do not edit) --- markers let the generator rewrite just its own blocks without disturbing hand-written content in go.mod.
go.sum drift
Testing the Go side
Sibling Go modules are self-contained - they don’t need the app shell checked out to compile or test:
cd ../example/server
go test ./...
The module’s go.mod pins the same PocketBase version the app shell uses, so test code hits the same API surface it will in production. Once linked, the replace directive points the app shell’s import of tinycld.org/packages/example at this same directory, so any test that passes here also reflects what the app shell will build against.
For the package-side concerns (the Register function signature, the directory layout, what to import), see Server.