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

140 lines
4.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
title: Version Your Schema with Go Migrations
impact: HIGH
impactDescription: Guarantees repeatable, transactional schema evolution and eliminates manual dashboard changes in production
tags: go, migrations, schema, database, migratecmd, extending
---
## Version Your Schema with Go Migrations
PocketBase ships with a `migratecmd` plugin that generates versioned `.go` migration files, applies them automatically on `serve`, and lets you roll back with `migrate down`. Because the files are compiled into your binary, no extra migration tool is needed.
**Incorrect (one-off SQL or dashboard changes in production):**
```go
// ❌ Running raw SQL directly at startup without a migration file
// the change is applied every restart and has no rollback path.
app.OnServe().BindFunc(func(se *core.ServeEvent) error {
_, err := app.DB().NewQuery(
"ALTER TABLE posts ADD COLUMN summary TEXT DEFAULT ''",
).Execute()
return err
})
// ❌ Forgetting to import the migrations package means
// registered migrations are never executed.
package main
import (
"github.com/pocketbase/pocketbase"
"github.com/pocketbase/pocketbase/plugins/migratecmd"
// _ "myapp/migrations" ← omitted: migrations never run
)
```
**Correct (register migratecmd, import migrations package):**
```go
// main.go
package main
import (
"log"
"os"
"github.com/pocketbase/pocketbase"
"github.com/pocketbase/pocketbase/plugins/migratecmd"
"github.com/pocketbase/pocketbase/tools/osutils"
// Import side-effects only; this registers all init() migrations.
_ "myapp/migrations"
)
func main() {
app := pocketbase.New()
migratecmd.MustRegister(app, app.RootCmd, migratecmd.Config{
// Automigrate generates a new .go file whenever you make
// collection changes in the Dashboard (dev-only).
Automigrate: osutils.IsProbablyGoRun(),
})
if err := app.Start(); err != nil {
log.Fatal(err)
}
}
```
**Create and write a migration:**
```bash
# Create a blank migration file in ./migrations/
go run . migrate create "add_summary_to_posts"
```
```go
// migrations/1687801090_add_summary_to_posts.go
package migrations
import (
"github.com/pocketbase/pocketbase/core"
m "github.com/pocketbase/pocketbase/migrations"
)
func init() {
m.Register(func(app core.App) error {
// app is a transactional App instance safe to use directly.
collection, err := app.FindCollectionByNameOrId("posts")
if err != nil {
return err
}
collection.Fields.Add(&core.TextField{
Name: "summary",
Required: false,
})
return app.Save(collection)
}, func(app core.App) error {
// Optional rollback
collection, err := app.FindCollectionByNameOrId("posts")
if err != nil {
return err
}
collection.Fields.RemoveByName("summary")
return app.Save(collection)
})
}
```
**Snapshot all collections (useful for a fresh repo):**
```bash
# Generates a migration file that recreates your current schema from scratch.
go run . migrate collections
```
**Clean up dev migration history:**
```bash
# Remove _migrations table entries that have no matching .go file.
# Run after squashing or deleting intermediate dev migration files.
go run . migrate history-sync
```
**Apply / roll back manually:**
```bash
go run . migrate up # apply all unapplied migrations
go run . migrate down 1 # revert the last applied migration
```
**Key details:**
- Migration functions receive a **transactional** `core.App` treat it as the database source of truth. Never use the outer `app` variable inside migration callbacks.
- New unapplied migrations run automatically on every `serve` start no manual step in production.
- `Automigrate: osutils.IsProbablyGoRun()` limits auto-generation to `go run` (development) and prevents accidental file creation in production binaries.
- Prefer the collection API (`app.Save(collection)`) over raw SQL `ALTER TABLE` so PocketBase's internal schema cache stays consistent.
- Commit all generated `.go` files to version control; do **not** commit `pb_data/`.
Reference: [Extend with Go Migrations](https://pocketbase.io/docs/go-migrations/)