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 a generated go.work and into the generated tinycld/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.
Core’s Go module
@tinycld/core is a nested member inside the tinycld repo (at tinycld/core/), and its Go side is the module tinycld.org/core at tinycld/core/server/, exporting coreserver (the registration orchestrator) plus subsystems like notify, push, mailer, audit, textextract, thumbnails. The app server (tinycld/server/main.go) is the module tinycld.org/app and consumes core via a hand-written replace directive in tinycld/server/go.mod:
require tinycld.org/core v0.0.0
replace tinycld.org/core => ../core/server
That path is relative to tinycld/server/ and points at the nested core’s server/ subdirectory at ~/code/tinycld/tinycld/core/server/.
Declaring a package module
Two fields in a feature’s 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. A feature’s go.mod requires tinycld.org/core v0.0.0; it does not need its own replace directive, because the generated go.work resolves every module’s location.
What the generator writes
On each pnpm run packages:generate (and on the workspace-root pnpm install), for every present feature with a server field, the generator:
-
Regenerates
tinycld/server/package_extensions.go, a small Go file whoseregisterPackageExtensions(app)calls each package’sRegister(app). The app shell’smain.goinvokes it (RegisterExtras: registerPackageExtensions) so every present package gets a chance to wire in hooks, endpoints, and workers before the server starts.// Code generated by tinycld/scripts/generate.ts. DO NOT EDIT. package main import ( "github.com/pocketbase/pocketbase" example "tinycld.org/packages/example" ) func registerPackageExtensions(app *pocketbase.PocketBase) { example.Register(app) } -
Writes
tinycld/server/go.work, a Go workspace file listing the app, core, and each feature server module by its on-disk path (resolved through the workspacenode_modules/@tinycld/*symlinks):go 1.25.0 use ( . ../../node_modules/@tinycld/core/server ../../node_modules/@tinycld/example/server )The
go.workfile is written only when at least one present feature ships a server; it’s removed when none do. Because module locations come fromgo.work, no per-packagereplacedirectives are appended togo.mod- the file stays hand-authored. -
Writes
tinycld/server/bundled-packages.json, the seed manifest core’s Go server uses to hydrate itspkg_registrycollection at boot.
Testing the Go side
A feature’s Go module is self-contained - it pins the same PocketBase version core uses, so test code hits the same API surface it will in production:
cd ~/code/tinycld/example/server
go test ./...
Within an assembled workspace, the generated go.work ties the feature module, core, and the app together, so a build from tinycld/server/ reflects exactly what the app ships. Core also has its own Go tests under tinycld/core/server/**/_test.go — run those from tinycld/core/server/ with go test ./....
For the package-side concerns (the Register function signature, the directory layout, what to import), see Server.