go-migrationgo-migration
Getting Started
Documentation

Quick Start

Create and run your first database migration with go-migration in minutes.

Quick Start

This guide walks you through creating and running your first migration with go-migration v1.0.0. By the end, you'll have a users table in your database.

Initialize Your Project

The fastest way to get started is with go-migration init. This command scaffolds all the directories and files you need:

bash
go-migration init

This creates:

  • database/migrations/, database/seeders/, database/factories/ directories
  • cmd/migrator/main.go — entry point with migrator.Run()
  • migration.json — config file with environment variable placeholders (JSON format)

The command auto-detects your module path from go.mod. See the CLI Reference for flags like --module and --force.

If you prefer to set up manually, you can skip this step and create the config file and directories yourself — see the steps below.

Create a Config File

If you ran go-migration init, a migration.json was already created in your project root with sensible defaults. Open it and fill in your database credentials:

migration.json
{
  "default": "postgres",
  "connections": {
    "postgres": {
      "driver": "postgres",
      "host": "${DB_HOST}",
      "port": 5432,
      "database": "${DB_NAME}",
      "username": "${DB_USER}",
      "password": "${DB_PASSWORD}",
      "sslmode": "disable"
    }
  },
  "migration_dir": "database/migrations",
  "seeder_dir": "database/seeders",
  "factory_dir": "database/factories",
  "migration_table": "migrations",
  "log_level": "info"
}

If you prefer YAML, create a go-migration.yaml manually. Note: YAML format is deprecated since v1.0.0 and will show a deprecation warning when loaded.

go-migration.yaml
default: postgres

connections:
  postgres:
    driver: postgres
    host: ${DB_HOST}
    port: 5432
    database: ${DB_NAME}
    username: ${DB_USER}
    password: ${DB_PASSWORD}
    sslmode: disable

migration_dir: database/migrations
seeder_dir: database/seeders
factory_dir: database/factories
migration_table: migrations
log_level: info

${VAR_NAME} placeholders are replaced with environment variable values at load time. See Configuration for all supported fields.

Define a Migration

Create a migration file with an init() function for auto-registration, plus Up and Down methods:

database/migrations/2025_01_15_143022_1234_create_users_table.go
package migrations

import (
    "github.com/gopackx/go-migration/migrator"
    "github.com/gopackx/go-migration/schema"
)

func init() {
    migrator.AutoRegister("2025_01_15_143022_1234_create_users_table", &CreateUsersTable{})
}

type CreateUsersTable struct{}

func (m *CreateUsersTable) Up(s *schema.Builder) error {
    return s.Create("users", func(bp *schema.Blueprint) {
        bp.ID()
        bp.String("name", 255)
        bp.String("email", 255).Unique()
        bp.Boolean("active").Default(true)
        bp.Timestamps()
    })
}

func (m *CreateUsersTable) Down(s *schema.Builder) error {
    return s.Drop("users")
}

The init() function registers the migration automatically when the package is imported. The Up method creates the table, and Down reverses it.

Migration filenames in v1.0.0 use the format YYYY_MM_DD_HHMMSS_RRRR_description.go, where RRRR is a random 4-digit number to prevent timestamp collisions. The make:migration command generates this automatically. The old format (YYYYMMDDHHMMSS_description.go) is still supported.

Set Up and Run

migrator.Run() is the all-in-one CLI runner introduced in v1.0.0. It handles config loading, database connection, auto-discovery of migrations and seeders, grammar auto-resolution, and command dispatch — all in a single call.

main.go
package main

import (
    "github.com/gopackx/go-migration/migrator"
    _ "your-project/database/migrations" // blank import triggers init() registration
    _ "your-project/database/seeders"    // blank import triggers seeder init() registration
)

func main() {
    migrator.Run()
}

That's it. migrator.Run() will:

  1. Parse CLI arguments (migrate, db:seed, migrate:fresh, etc.)
  2. Load migration.json (with ${ENV} interpolation, falls back to go-migration.yaml)
  3. Open a database connection
  4. Auto-discover all registered migrations and seeders
  5. Resolve the correct grammar from the driver name
  6. Execute the requested command

If you need more control, you can set up the migrator manually:

main.go
package main

import (
    "database/sql"
    "log"

    _ "github.com/lib/pq" // PostgreSQL driver

    "github.com/gopackx/go-migration/migrator"
    _ "your-project/database/migrations" // blank import triggers init() registration
)

func main() {
    db, err := sql.Open("postgres", "postgres://user:password@localhost:5432/mydb?sslmode=disable")
    if err != nil {
        log.Fatal(err)
    }
    defer db.Close()

    m := migrator.New(db, migrator.WithAutoDiscover())

    if err := m.Up(); err != nil {
        log.Fatal(err)
    }

    log.Println("Migrations completed successfully!")
}

The blank import _ "your-project/database/migrations" triggers all init() functions, and WithAutoDiscover() loads them into the migrator.

Run It

bash
# With migrator.Run() — use CLI commands directly
go run main.go migrate

# Or run all pending migrations with manual setup
go run main.go

go-migration will:

  1. Create a migrations tracking table (if it doesn't exist)
  2. Check which migrations have already run
  3. Execute the Up method of any pending migrations
  4. Record the migration in the tracking table with a batch number

Replace the connection string or environment variables with your actual database credentials. The example above uses PostgreSQL — see Database Grammars for MySQL and SQLite setup.

Using migrator.Run()

With migrator.Run(), your binary becomes a full CLI tool. All standard commands are available:

bash
go run main.go migrate              # Run pending migrations
go run main.go migrate --seed       # Run pending migrations + seeders
go run main.go migrate:rollback     # Roll back last batch
go run main.go migrate:fresh        # Drop all tables + re-migrate
go run main.go migrate:refresh      # Reset + re-migrate
go run main.go migrate:refresh --seed # Reset + re-migrate + seed
go run main.go migrate:status       # Show migration status
go run main.go db:seed              # Run all seeders
go run main.go db:seed --class=UserSeeder  # Run specific seeder
go run main.go db:seed --tag=core   # Run seeders by tag
go run main.go make:migration create_posts # Scaffold a migration file

migrator.Run() uses smart command classification — commands like version, help, and make:* skip database setup entirely, so they work even without a valid database connection.

New in v1.0.0: The --seed flag on migrate and migrate:refresh, and the --tag flag on db:seed for running seeders by tag. See the CLI Reference for all available flags.

Using a Different Database

go-migration supports PostgreSQL, MySQL, and SQLite. With migrator.Run(), the grammar is auto-resolved from the driver field in your config file — no manual grammar selection needed.

For manual setup, swap the driver import and grammar:

MySQL example
import (
    "database/sql"
    _ "github.com/go-sql-driver/mysql"

    "github.com/gopackx/go-migration/migrator"
    "github.com/gopackx/go-migration/schema/grammars"
    _ "your-project/database/migrations"
)

func main() {
    db, _ := sql.Open("mysql", "user:password@tcp(localhost:3306)/mydb")

    m := migrator.New(db,
        migrator.WithGrammar(&grammars.MySQLGrammar{}),
        migrator.WithAutoDiscover(),
    )

    // Run as before...
}
SQLite example
import (
    "database/sql"
    _ "github.com/mattn/go-sqlite3"

    "github.com/gopackx/go-migration/migrator"
    "github.com/gopackx/go-migration/schema/grammars"
    _ "your-project/database/migrations"
)

func main() {
    db, _ := sql.Open("sqlite3", "./app.db")

    m := migrator.New(db,
        migrator.WithGrammar(&grammars.SQLiteGrammar{}),
        migrator.WithAutoDiscover(),
    )

    // Run as before...
}

Rolling Back

To undo the last batch of migrations:

go
// Roll back the last batch
if err := m.Rollback(0); err != nil {
    log.Fatal(err)
}

This calls the Down method on each migration in the last batch, in reverse order.

What's Next?