Skip to main content

Use Queen as a Library

Use the library API when migrations should run from your application startup, tests, or an internal deploy tool.

Create a Queen instance​

db, err := sql.Open("pgx", os.Getenv("DATABASE_URL"))
if err != nil {
return err
}
defer db.Close()

q := queen.New(postgres.New(db))
defer q.Close()

queen.New accepts options:

q := queen.New(
postgres.New(db),
queen.WithLogger(logger),
queen.WithTap(tap.NewJSONSink(os.Stdout)),
)

Configure behavior​

q := queen.NewWithConfig(postgres.New(db), &queen.Config{
TableName: "queen_migrations",
LockTimeout: 10 * time.Minute,
SkipLock: false,
})

Use SkipLock: true only for controlled single-runner cases. For production PostgreSQL, keep locking enabled.

Config fields:

FieldDefaultPurpose
TableNamequeen_migrationsMigration metadata table.
LockTimeout30mDriver lock acquisition timeout.
SkipLockfalseSkip driver lock acquisition.
NamingnilOptional version naming policy.
IsolationLevelsql.LevelDefaultDefault transaction isolation level.
AllowUnknownAppliedfalsePermit applied database versions absent from this binary's registry during an intentional rolling deployment.
AllowOutOfOrderfalsePermit applying an older pending version after a newer version.

Register migrations​

func Register(q *queen.Queen) {
q.MustAdd(queen.M{
Version: "001",
Name: "create_users",
UpSQL: `CREATE TABLE users (id SERIAL PRIMARY KEY, email TEXT NOT NULL);`,
DownSQL: `DROP TABLE users;`,
})
}

MustAdd is convenient during program startup. Use Add if you want to return registration errors instead of panicking.

Apply and rollback​

err := q.Up(ctx) // apply all pending migrations
err = q.UpSteps(ctx, 2) // apply up to 2 pending migrations
err = q.Down(ctx, 1) // rollback one migration
err = q.Reset(ctx) // rollback all applied migrations

Down(ctx, n <= 0) rolls back exactly one latest migration. Use Reset(ctx) for "everything".

Inspect before running​

plans, err := q.DryRun(ctx, queen.DirectionUp, 0)
if err != nil {
return err
}

for _, p := range plans {
fmt.Printf("%s %s destructive=%v\n", p.Version, p.Name, p.IsDestructive)
}

Validate(ctx) checks registration validity, applied checksums, incomplete migrations, unknown applied versions, and migration order before you run migrations. By default, Up, Down, and Reset also stop on unsafe applied history.

Runtime API​

MethodPurpose
Add(m queen.M) errorRegister one migration.
MustAdd(m queen.M)Register one migration and panic on invalid input.
Up(ctx)Apply all pending migrations.
UpSteps(ctx, n)Apply up to n pending migrations; n <= 0 means all.
Down(ctx, n)Roll back n latest migrations; n <= 0 means one.
Reset(ctx)Roll back all applied migrations.
Status(ctx)Return status for all registered migrations.
Validate(ctx)Validate registration and checksum drift.
DryRun(ctx, direction, limit)Return migration plans without executing.
Explain(ctx, version)Return detailed plan for one migration.
FindMigration(version)Return a defensive copy of one registered migration.
RegisteredMigrations()Return copies of registered migrations in version order.
ResolveIncomplete(ctx, version, state)Resolve an interrupted non-transactional migration after inspecting the database.
Driver()Return the underlying driver.
SQLDB()Return the underlying *sql.DB when the driver exposes it.
Close()Close driver resources.

Long-running public operations on a single Queen instance are serialized internally. For production deploys, still run one intentional migrator job per environment.

Status values​

Status(ctx) reports:

StatusMeaning
pendingRegistered in code, not recorded in the database.
appliedRegistered and recorded with matching checksum.
modifiedApplied, but the registered checksum differs from the stored checksum.
incompleteA non-transactional migration needs manual inspection and recovery.

modified and incomplete block migration execution until you resolve the problem.

pgxpool adapter​

If your app already owns a pgxpool.Pool, use the adapter:

pool, err := pgxpool.New(ctx, os.Getenv("DATABASE_URL"))
if err != nil {
return err
}
defer pool.Close()

q := queen.New(postgres.NewFromPool(pool))
defer q.Close()

Closing Queen closes the database/sql adapter handle, not the caller-owned pool.

Testing helper​

Queen includes a small test helper:

func TestMigrations(t *testing.T) {
driver := setupTestDriver(t)
q := queen.NewTest(t, driver)

migrations.Register(q.Queen)
q.TestUpDown()
}

Useful helper methods include MustUp, MustDown, MustReset, MustValidate, TestUpDown, and TestRollback.