go-migrationGo Migration
Seeders
Documentation

Running Seeders

Learn how to register and run seeders using runner.RunAll() and runner.Run() methods.

Running Seeders

Once you've defined your seeders, use the seeder runner to register and execute them.

Auto-Registration with init()

Similar to migrations, seeders can be auto-registered from init() functions using seeder.AutoRegister(). This is the recommended approach when using the CLI entry point app.Run().

seeders/user_seeder.go
package seeders

import (
    "database/sql"

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

func init() {
    seeder.AutoRegister("UserSeeder", &UserSeeder{})
}

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
}

When using app.Run(), auto-registered seeders are discovered automatically — no manual registration needed. Just blank-import the seeders package in your main.go:

cmd/migrator/main.go
package main

import (
    "github.com/gopackx/go-migration/pkg/cli/app"

    _ "github.com/lib/pq"

    _ "your-project/migrations"
    _ "your-project/seeders" // triggers init() auto-registration
)

func main() {
    app.Run()
}

The CLI's db:seed, db:seed:rollback, and db:seed:truncate commands hold the cross-process migration lock while they run, so seeders never run concurrently with each other or with migrations. They accept --lock-timeout and --no-lock.

seeder.AutoRegister() panics on duplicate names and on empty/whitespace-only names, just like migrator.AutoRegister(). This ensures configuration mistakes are caught at startup.

Manual Registration

For more control, build a Registry, register your seeders on it, then hand it to a Runner:

main.go
package main

import (
    "database/sql"
    "log"

    _ "github.com/lib/pq"

    "github.com/gopackx/go-migration/pkg/seeder"
    "your-project/seeders"
)

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

    reg := seeder.NewRegistry()
    if err := reg.Register("UserSeeder", &seeders.UserSeeder{}); err != nil {
        log.Fatal(err)
    }
    if err := reg.Register("PostSeeder", &seeders.PostSeeder{}); err != nil {
        log.Fatal(err)
    }
    if err := reg.Register("RoleSeeder", &seeders.RoleSeeder{}); err != nil {
        log.Fatal(err)
    }

    // The logger may be nil to skip logging.
    runner := seeder.NewRunner(reg, db, nil)

    // Run all seeders
    if err := runner.RunAll(); err != nil {
        log.Fatal(err)
    }

    log.Println("All seeders completed successfully!")
}

Runner Options

seeder.NewRunner accepts optional RunnerOptions after the logger:

go
func NewRunner(registry *Registry, db *sql.DB, logger Logger, opts ...RunnerOption) *Runner

seeder.WithDialect(d) sets the SQL dialect the runner uses for the statements it generates itself — currently the identifier quoting in runner.Truncate(). The default is seeder.DialectPostgres; pass the dialect that matches your database:

go
runner := seeder.NewRunner(reg, db, nil, seeder.WithDialect(seeder.DialectMySQL))
DatabaseOption
PostgreSQLseeder.WithDialect(seeder.DialectPostgres) (default)
MySQLseeder.WithDialect(seeder.DialectMySQL)
SQLiteseeder.WithDialect(seeder.DialectSQLite)

The CLI picks the dialect from the connection's driver automatically.

A Runner created in your own code does not take the migration lock. If seeders may run while another process migrates, wrap the call in m.Lock(ctx) — see Locking Your Own Work.

Registry.Register() returns an error — it wraps seeder.ErrInvalidSeederName for empty names and seeder.ErrDuplicateSeeder for duplicates. Unlike AutoRegister(), it does not panic.

Running All Seeders

runner.RunAll() executes every registered seeder:

go
if err := runner.RunAll(); err != nil {
    log.Fatal(err)
}

Execution Order

Without dependencies, seeders run in a deterministic order (sorted by name). Dependencies override this so that a seeder always runs after the seeders it depends on.

Sequential Execution

Each seeder's Run method is called one at a time. If a seeder returns an error, execution stops immediately.

Error Handling

RunAll() returns the first error encountered. Seeders that completed before the failure remain applied.

Running a Specific Seeder

Use runner.Run("SeederName") to execute a single seeder by its registered name:

go
// Run only the UserSeeder
if err := runner.Run("UserSeeder"); err != nil {
    log.Fatal(err)
}

This is useful when you need to re-seed a specific table without running all seeders again.

Complete Example

main.go
package main

import (
    "database/sql"
    "log"
    "os"

    _ "github.com/lib/pq"

    "github.com/gopackx/go-migration/pkg/seeder"
    "your-project/seeders"
)

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

    reg := seeder.NewRegistry()
    for name, s := range map[string]seeder.Seeder{
        "UserSeeder": &seeders.UserSeeder{},
        "RoleSeeder": &seeders.RoleSeeder{},
        "PostSeeder": &seeders.PostSeeder{},
    } {
        if err := reg.Register(name, s); err != nil {
            log.Fatal(err)
        }
    }

    runner := seeder.NewRunner(reg, db, nil)

    // Check if a specific seeder was requested
    if len(os.Args) > 1 {
        name := os.Args[1]
        log.Printf("Running seeder: %s", name)
        if err := runner.Run(name); err != nil {
            log.Fatal(err)
        }
    } else {
        log.Println("Running all seeders...")
        if err := runner.RunAll(); err != nil {
            log.Fatal(err)
        }
    }

    log.Println("Seeding complete!")
}

The name passed to runner.Run() must match the name used in reg.Register() exactly. If the name doesn't match any registered seeder, seeder.ErrSeederNotFound is returned.

What's Next?

  • Dependencies — control the execution order of seeders with dependency declarations