Skip to content
You are reading the v0.20 documentation. Go to the latest version (v1.0)

Database

Migrations

Versioned schema changes in Go with a portable schema builder, checksums, and vukem migrate commands.

Last updated:

Migrations are plain Go values listed in one registry function. There is no init() registration: vukem make:migration writes the file and appends it to the list.

Creating a migration#

Terminal
vukem make:migration create_comments_table
vukem make:migration add_slug_to_posts_table

The stub depends on the name: create_<table>_table creates a table, add_x_to_<table>_table / alter_<table>_table alter one, anything else is blank.

database/migrations/20261016120000_create_comments_table.go
func m20261016120000CreateCommentsTable() schema.Migration {
	return schema.Migration{
		ID: "20261016120000_create_comments_table",
		Up: func(s *schema.Schema) error {
			return s.Create("comments", func(t *schema.Table) {
				t.ID()
				t.ForeignID("post_id").Constrained().CascadeOnDelete()
				t.Text("body")
				t.Boolean("approved").Default(false)
				t.Timestamps()
				t.Index("post_id", "approved")
			})
		},
		Down: func(s *schema.Schema) error { return s.DropIfExists("comments") },
	}
}
database/migrations/registry.go
func All() []schema.Migration {
	return []schema.Migration{
		m20260101000000CreatePostsTable(),
		m20261016120000CreateCommentsTable(), // appended by make:migration
	}
}

bootstrap.New passes migrations.All() to vukem.WithMigrations.

Schema builder#

SchemaTable columnsColumn modifiers
Create, Table (alter), Drop, DropIfExists, Rename, HasTable, RawID, String(name, len?), Text, Integer, BigInteger, Boolean, Decimal(p, s), Float, Date, Timestamp, Timestamps, SoftDeletes, JSON, UUID, ULID, Binary, Enum(name, values...), ForeignIDNullable, Default, Unique, Index, Constrained(table?), CascadeOnDelete, NullOnDelete, RestrictOnDelete

Altering: t.RenameColumn(from, to), t.DropColumn(names...), t.Index(cols...), t.UniqueIndex(cols...), t.DropIndex(name).

DDL is generated per dialect (SQLite and PostgreSQL). Table and column names are validated, never taken from user input.

Running migrations#

CommandEffect
vukem migrateRun pending migrations in one batch
vukem migrate:rollback [--step N]Roll back the last batch, or the last N migrations
vukem migrate:resetRoll back everything
vukem migrate:freshDrop all tables and re-run every migration
vukem migrate:status [--json]Which migrations ran, in which batch, and when

migrate:reset and migrate:fresh refuse to run with APP_ENV=production unless --force is given.

Each applied migration stores a checksum of the SQL it produced. migrate:status flags migrations whose SQL changed after they ran and migrations recorded in the database but missing from code.

In code, app.Migrator() returns a *schema.Migrator with Up, Rollback, Reset, Fresh and Status; vukemtest.New uses it to migrate the test database.