Project Structure
Queen migrations are Go code, so the cleanest layout is a normal Go package for migrations plus a small CLI entrypoint.
myapp/
cmd/
migrate/
main.go
migrations/
migrations.go
001_create_users.go
002_add_user_slug.go
003_backfill_profiles.go
go.mod
Migration registry
migrations/migrations.go
package migrations
import "github.com/yaop-labs/queen"
func Register(q *queen.Queen) {
Register001CreateUsers(q)
Register002AddUserSlug(q)
Register003BackfillProfiles(q)
}
SQL migration file
migrations/001_create_users.go
package migrations
import "github.com/yaop-labs/queen"
func Register001CreateUsers(q *queen.Queen) {
q.MustAdd(queen.M{
Version: "001",
Name: "create_users",
UpSQL: `
CREATE TABLE users (
id BIGSERIAL PRIMARY KEY,
email TEXT NOT NULL UNIQUE,
created_at TIMESTAMP NOT NULL DEFAULT NOW()
);
`,
DownSQL: `DROP TABLE users;`,
})
}
Go-function migration file
migrations/003_backfill_profiles.go
package migrations
import (
"context"
"database/sql"
"github.com/yaop-labs/queen"
)
func Register003BackfillProfiles(q *queen.Queen) {
q.MustAdd(queen.M{
Version: "003",
Name: "backfill_profiles",
ManualChecksum: "backfill-profiles-v1",
UpFunc: up003BackfillProfiles,
})
}
func up003BackfillProfiles(ctx context.Context, tx *sql.Tx) error {
_, err := tx.ExecContext(ctx, `
INSERT INTO profiles (user_id, display_name)
SELECT id, email FROM users
`)
return err
}
CLI entrypoint
cmd/migrate/main.go
package main
import (
"github.com/yaop-labs/queen/cli"
"myapp/migrations"
_ "github.com/jackc/pgx/v5/stdlib"
)
func main() {
cli.Run(migrations.Register)
}
This gives you one migration registry reused by:
- local development through
go run ./cmd/migrate ...; - CI/CD through a built migrator binary;
- application startup if your app calls Queen directly.
A complete copy-paste version of this layout lives in examples/basic-migrator in this documentation repo. For the runtime model behind this layout, see Architecture. For database-specific migration examples, see Database Examples.