| name | pocketbase-migrations |
| description | Comprehensive guide for PocketBase migrations (0.20+) using Go-based migration system. Use when creating or modifying PocketBase collections, managing fields, setting up relations, or writing migration files. |
PocketBase Migrations
Guide for creating PocketBase migrations using the Go-based system (0.20+).
Core Workflow
Critical Migration Pattern
ALWAYS follow this workflow:
- Write ONE migration at a time
- Execute immediately with
mise run migrate
- Verify it worked with
mise run show-collections
- Only then write the next migration
- Run
mise run backup before destructive changes
Never write multiple migrations without running them between each one.
Migration Structure
package migrations
import (
"github.com/pocketbase/pocketbase/core"
"github.com/pocketbase/pocketbase/tools/types"
m "github.com/pocketbase/pocketbase/migrations"
)
func init() {
m.Register(func(app core.App) error {
return nil
}, func(app core.App) error {
return nil
})
}
Collection Operations
Create Base Collection
collection := core.NewBaseCollection("posts")
collection.Fields.Add(&core.TextField{
Name: "title",
Required: true,
Max: 100,
})
collection.ListRule = types.Pointer("@request.auth.id != ''")
collection.CreateRule = types.Pointer("@request.auth.id != ''")
collection.UpdateRule = types.Pointer("@request.auth.id = author")
collection.DeleteRule = types.Pointer("@request.auth.id = author")
return app.Save(collection)
Update Existing Collection
collection, err := app.FindCollectionByNameOrId("posts")
if err != nil {
return err
}
collection.Fields.Add(&core.DateField{
Name: "publishedAt",
})
collection.Fields.RemoveByName("oldField")
collection.UpdateRule = types.Pointer("@request.auth.id = author")
return app.Save(collection)
Common Field Types
See references/field-types.md for complete field type reference.
Quick Reference
&core.TextField{Name: "title", Required: true, Max: 100}
&core.NumberField{Name: "price", Min: types.Pointer(0.0)}
&core.BoolField{Name: "isActive"}
&core.EmailField{Name: "email", Required: true}
&core.DateField{Name: "publishedAt"}
&core.AutodateField{Name: "created", OnCreate: true}
&core.SelectField{
Name: "status",
Values: []string{"draft", "published"},
MaxSelect: 1,
}
&core.FileField{
Name: "avatar",
MaxSelect: 1,
MaxSize: 5242880,
}
&core.EditorField{
Name: "content",
MaxSize: 1048576,
}
&core.JSONField{Name: "metadata", MaxSize: 65535}
Working with Relations
Best Practices
- Create collections in dependency order
- Fetch collections before creating relations
- Use actual collection IDs for relations
- Handle self-referencing relations in separate migrations
Relation Field
authorsCollection, err := app.FindCollectionByNameOrId("authors")
if err != nil {
return err
}
collection.Fields.Add(&core.RelationField{
Name: "author",
Required: true,
MaxSelect: 1,
CollectionId: authorsCollection.Id,
CascadeDelete: true,
})
collection.Fields.Add(&core.RelationField{
Name: "creator",
CollectionId: "_pb_users_auth_",
MaxSelect: 1,
})
Migration Order for Relations
Create collections in this order:
- Independent collections (no relations)
- Collections depending on system collections
- Collections with relations to other custom collections
- Self-referencing relations (separate migration)
Example order:
1_create_categories.go # Independent
2_create_authors.go # Depends on system users
3_create_posts.go # Depends on authors & categories
4_create_comments.go # Depends on posts & users
5_add_parent_to_comments.go # Self-referencing
Collection Rules
Rule Types
ListRule - List records
ViewRule - View individual records
CreateRule - Create records
UpdateRule - Update records
DeleteRule - Delete records
Common Patterns
types.Pointer("")
types.Pointer("@request.auth.id != ''")
types.Pointer("@request.auth.id = author")
types.Pointer("status = 'published' || author = @request.auth.id")
types.Pointer("author.user = @request.auth.id")
View Collections
viewQuery := `
SELECT
posts.id,
posts.title,
users.name as author_name
FROM posts
JOIN users ON posts.author = users.id
`
collection := core.NewViewCollection("posts_with_authors", viewQuery)
return app.Save(collection)
Error Handling
Always check errors:
collection, err := app.FindCollectionByNameOrId("posts")
if err != nil {
return err
}
if err := app.Save(collection); err != nil {
return err
}
Task Commands
mise run makemigration <name> - Create new migration file
mise run migrate - Run pending migrations
mise run migratedown - Rollback last migration
mise run show-collections - Display collections
mise run backup - Backup database to /tmp
Best Practices
- Import types package for nullable values:
"github.com/pocketbase/pocketbase/tools/types"
- Use
types.Pointer() for rule assignments and pointer values
- Check collection existence before creating relations
- Validate field names don't conflict with system fields (id, created, updated)
- Handle errors immediately after operations
- Never skip migration testing - run each one before writing the next
- Backup before destructive changes
Common Gotchas
- Use
MaxSelect (not Max) for RelationField
- Use
MaxSize (not Max) for EditorField
- System users collection ID:
"_pb_users_auth_"
- For number min/max:
types.Pointer(0.0)
- Empty rules need:
types.Pointer("")
- Self-referencing relations need separate migrations
Additional Resources