go-migration

module
v1.0.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 5, 2026 License: MIT

README

go-migration

Go Reference Go Report Card

A Laravel-inspired database migration and seeding system for Go. Struct-based migrations, fluent schema builder, factory-based seeders with faker, batch tracking, hooks, and multi-database support — works with any framework or standalone.

Features

  • Struct-based migrations with Up() / Down() methods — type-safe, no raw SQL files
  • Fluent schema builder for tables, columns, indexes, and foreign keys
  • Per-migration transactions with opt-out support
  • Batch tracking and granular rollback (by batch or step count)
  • Before/after migration hooks
  • Seeder system with dependency resolution and circular dependency detection
  • Generic factory pattern with faker for realistic test data
  • Multi-database connection management with pooling
  • CLI with Laravel-style commands (migrate, migrate:rollback, make:migration, db:seed, etc.)
  • Grammars for PostgreSQL, MySQL, and SQLite
  • Framework-agnostic — depends only on database/sql

Installation

go get github.com/gopackx/go-migration

Quick Start

Define a migration
package main

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

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")
}
Register and run migrations
package main

import (
    "database/sql"
    "log"

    _ "github.com/lib/pq"
    "github.com/gopackx/go-migration/pkg/migrator"
    "github.com/gopackx/go-migration/pkg/schema/grammars"
)

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

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

    // Register migrations
    m.Register("20260101120000_create_users", &CreateUsersTable{})

    // Run all pending migrations
    if err := m.Up(); err != nil {
        log.Fatal(err)
    }
}

Schema Builder

The schema builder provides a fluent, database-agnostic API for defining tables:

// Create table
s.Create("posts", func(bp *schema.Blueprint) {
    bp.ID()
    bp.String("title", 255)
    bp.Text("body").Nullable()
    bp.BigInteger("author_id").Unsigned()
    bp.Decimal("rating", 3, 2).Default(0)
    bp.Boolean("published").Default(false)
    bp.JSON("metadata").Nullable()
    bp.Timestamps()
    bp.SoftDeletes()

    // Indexes
    bp.Index("title")
    bp.UniqueIndex("title", "author_id")

    // Foreign keys
    bp.Foreign("author_id").References("id").On("users").OnDelete("CASCADE")
})

// Alter table
s.Alter("posts", func(bp *schema.Blueprint) {
    bp.String("slug", 255).Unique()
    bp.DropColumn("metadata")
})

// Other operations
s.Drop("posts")
s.DropIfExists("posts")
s.Rename("posts", "articles")
s.HasTable("posts")       // bool, error
s.HasColumn("posts", "id") // bool, error
Supported column types

ID, String, Text, Integer, BigInteger, Boolean, Timestamp, Date, Decimal, Float, UUID, JSON, Binary

Column modifiers

.Nullable(), .Default(value), .Primary(), .Unique(), .Unsigned(), .AutoIncrement()

Migrator API

m := migrator.New(db,
    migrator.WithGrammar(&grammars.PostgresGrammar{}),
    migrator.WithTableName("migrations"),  // default: "migrations"
    migrator.WithLogger(myLogger),
)

m.Register("20260101120000_create_users", &CreateUsersTable{})

m.Up()              // Run all pending migrations
m.Rollback(0)       // Rollback last batch
m.Rollback(3)       // Rollback last 3 migrations
m.Reset()           // Rollback all migrations
m.Refresh()         // Reset + Up
m.Fresh()           // Drop all tables + Up (requires grammar)
m.Status()          // []MigrationStatus
Transaction opt-out

By default every migration runs in a transaction. To opt out, implement TransactionOption:

type LargeDataMigration struct{}

func (m *LargeDataMigration) DisableTransaction() bool { return true }
func (m *LargeDataMigration) Up(s *schema.Builder) error   { /* ... */ return nil }
func (m *LargeDataMigration) Down(s *schema.Builder) error { /* ... */ return nil }
Hooks
m.BeforeMigrate(func(name, direction string) error {
    log.Printf("About to run %s (%s)", name, direction)
    return nil
})

m.AfterMigrate(func(name, direction string, duration time.Duration) error {
    log.Printf("Completed %s (%s) in %v", name, direction, duration)
    return nil
})

Seeder System

import "github.com/gopackx/go-migration/pkg/seeder"

type UserSeeder struct{}

func (s *UserSeeder) Run(db *sql.DB) error {
    _, err := db.Exec("INSERT INTO users (name, email) VALUES ($1, $2)", "Alice", "alice@example.com")
    return err
}

// Optional: declare dependencies
type PostSeeder struct{}

func (s *PostSeeder) Run(db *sql.DB) error { /* ... */ return nil }
func (s *PostSeeder) DependsOn() []string  { return []string{"UserSeeder"} }
reg := seeder.NewRegistry()
reg.Register("UserSeeder", &UserSeeder{})
reg.Register("PostSeeder", &PostSeeder{})

runner := seeder.NewRunner(reg, db, nil)
runner.RunAll()           // Runs all seeders in dependency order
runner.Run("PostSeeder")  // Runs PostSeeder and its dependencies
Factory + Faker
import "github.com/gopackx/go-migration/pkg/seeder/factory"

type User struct {
    Name  string
    Email string
    Age   int
}

f := factory.NewFactory(func(fake factory.Faker) User {
    return User{
        Name:  fake.Name(),
        Email: fake.Email(),
        Age:   fake.IntBetween(18, 65),
    }
})

user := f.Make()          // Single instance
users := f.MakeMany(10)   // Slice of 10

// Named states
f.State("admin", func(fake factory.Faker, base User) User {
    base.Name = "Admin " + base.Name
    return base
})
admin := f.WithState("admin").Make()

Multi-Database Connections

import "github.com/gopackx/go-migration/pkg/database"
import "github.com/gopackx/go-migration/pkg/database/drivers"

mgr := database.NewManager()
mgr.RegisterDriver("postgres", &drivers.PostgresDriver{})
mgr.RegisterDriver("mysql", &drivers.MySQLDriver{})

mgr.AddConnection("primary", database.ConnectionConfig{
    Driver:   "postgres",
    Host:     "localhost",
    Port:     5432,
    Database: "myapp",
    Username: "user",
    Password: "pass",
})

mgr.AddConnection("analytics", database.ConnectionConfig{
    Driver:   "mysql",
    Host:     "localhost",
    Port:     3306,
    Database: "analytics",
    Username: "user",
    Password: "pass",
})

mgr.SetDefault("primary")

db, _ := mgr.Connection("primary")
analyticsDB, _ := mgr.Connection("analytics")
defer mgr.Close()

CLI

The package includes a CLI built with Cobra. Build it from cmd/migrator/:

go build -o migrator ./cmd/migrator

Available commands:

Command Description
migrate Run all pending migrations
migrate:rollback Rollback last batch (use --step N for N migrations)
migrate:reset Rollback all migrations
migrate:refresh Reset + migrate up
migrate:fresh Drop all tables + migrate up
migrate:status Show migration status
migrate:install Create the migration tracking table
make:migration Generate a migration file (--create or --table flags)
make:seeder Generate a seeder file
db:seed Run seeders (--class flag for specific seeder)
# Run migrations
./migrator migrate --config config.yaml

# Generate a migration with create-table scaffolding
./migrator make:migration create_orders --create orders

# Generate a migration with alter-table scaffolding
./migrator make:migration add_status_to_orders --table orders

# Rollback last 2 migrations
./migrator migrate:rollback --step 2

# Seed the database
./migrator db:seed
./migrator db:seed --class UserSeeder

Configuration

Supports YAML, JSON, or environment variables:

# config.yaml
default: primary
migration_table: migrations
migration_dir: migrations
seeder_dir: seeders
log_level: info
log_output: console

connections:
  primary:
    driver: postgres
    host: localhost
    port: 5432
    database: myapp
    username: user
    password: pass
    max_open_conns: 25
    max_idle_conns: 5
    conn_max_lifetime: 5m

Framework Integration

go-migration works with any Go framework — it only depends on database/sql. See the examples/ directory:

  • Gin — pass *sql.DB from your Gin setup
  • Echo — pass *sql.DB from your Echo setup
  • Fiber — pass *sql.DB from your Fiber setup
  • net/http — standard library, no framework

The pattern is always the same: open a *sql.DB, create a migrator.New(db), register migrations, call m.Up().

Supported Databases

  • PostgreSQL
  • MySQL
  • SQLite

Testing

go test ./...

The project uses property-based testing with pgregory.net/rapid alongside standard unit tests with testify.

License

MIT — see LICENSE.

Author

Andrian Prasetya — @andrianprasetya

Directories

Path Synopsis
cmd
migrator command
internal
generator
Package generator provides migration and seeder file generation using Go text/template and embedded template files.
Package generator provides migration and seeder file generation using Go text/template and embedded template files.
scanner
Package scanner provides functions to discover migration and seeder files in a directory by matching known naming patterns.
Package scanner provides functions to discover migration and seeder files in a directory by matching known naming patterns.
utils
Package utils provides minimal utility functions for string conversion, timestamp generation, and file system helpers.
Package utils provides minimal utility functions for string conversion, timestamp generation, and file system helpers.
pkg
cli
Package cli provides the command-line interface for go-migration.
Package cli provides the command-line interface for go-migration.
config
Package config provides configuration loading and validation for go-migration.
Package config provides configuration loading and validation for go-migration.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL