Files
shiftcraft/.claude/skills/pocketbase-best-practices/rules/ext-go-setup.md
2026-04-17 23:26:01 +00:00

2.9 KiB

title, impact, impactDescription, tags
title impact impactDescription tags
Set Up a Go-Extended PocketBase Application HIGH Foundation for all custom Go business logic, hooks, and routing go, extending, setup, main, bootstrap

Set Up a Go-Extended PocketBase Application

When extending PocketBase as a Go framework (v0.36+), the entry point is a small main.go that creates the app, registers hooks on OnServe(), and calls app.Start(). Avoid reaching for a global app variable inside hook handlers - use e.App instead so code works inside transactions.

Incorrect (global app reuse, no OnServe hook, bare http.Handler):

package main

import (
    "log"
    "net/http"

    "github.com/pocketbase/pocketbase"
)

var app = pocketbase.New() // global reference used inside handlers

func main() {
    // Routes registered directly via net/http - bypasses PocketBase's router,
    // middleware chain, auth, rate limiter and body limit
    http.HandleFunc("/hello", func(w http.ResponseWriter, r *http.Request) {
        w.Write([]byte("hello"))
    })

    if err := app.Start(); err != nil {
        log.Fatal(err)
    }
}

Correct (register routes inside OnServe, use e.App in handlers):

package main

import (
    "log"
    "net/http"
    "os"

    "github.com/pocketbase/pocketbase"
    "github.com/pocketbase/pocketbase/apis"
    "github.com/pocketbase/pocketbase/core"
)

func main() {
    app := pocketbase.New()

    app.OnServe().BindFunc(func(se *core.ServeEvent) error {
        // Serve static assets from ./pb_public (if present)
        se.Router.GET("/{path...}", apis.Static(os.DirFS("./pb_public"), false))

        // Custom API route - namespaced under /api/{yourapp}/ to avoid
        // colliding with built-in /api/collections, /api/realtime, etc.
        se.Router.GET("/api/myapp/hello/{name}", func(e *core.RequestEvent) error {
            return e.JSON(http.StatusOK, map[string]string{
                "message": "hello " + e.Request.PathValue("name"),
            })
        }).Bind(apis.RequireAuth())

        return se.Next()
    })

    if err := app.Start(); err != nil {
        log.Fatal(err)
    }
}

Project bootstrap:

go mod init myapp
go mod tidy
go run . serve           # development
go build && ./myapp serve  # production (statically linked binary)

Key details:

  • Requires Go 1.25.0+ (PocketBase v0.36.7+ bumped the minimum to Go 1.25.0).
  • PocketBase ships with the pure-Go modernc.org/sqlite driver - no CGO required by default.
  • If you need FTS5, ICU, or a custom SQLite build, pass core.DBConnect in pocketbase.NewWithConfig(...) - it is called twice (once for pb_data/data.db, once for pb_data/auxiliary.db).
  • Inside hooks, prefer e.App over a captured parent-scope app - the hook may run inside a transaction and the parent app would deadlock.

Reference: Extend with Go - Overview