go-migrationGo Migration
Package Reference
Documentation

Seeder

Complete package reference for the go-migration Seeder interface, Runner, and dependency system — all public methods with signatures, parameters, return types, and usage examples.

Seeder API

The seeder system lets you populate your database with test or default data. It consists of the Seeder interface, the Registry for registration, the Runner for execution, and an optional DependentSeeder interface for ordering.

Import path: github.com/gopackx/go-migration/pkg/seeder

For conceptual documentation, see Seeders.


Seeder Interface

Every seeder struct must implement this interface:

go
type Seeder interface {
    Run(db *sql.DB) error
}
MethodSignatureDescription
RunRun(db *sql.DB) errorExecutes the seeding logic

The Run method receives a *sql.DB connection and returns an error if seeding fails.

go
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
}

See Defining Seeders.


seeder.AutoRegister()

Registers a seeder in the global auto-registry. Intended to be called from init() functions in seeder files, matching the pattern used by migrator.AutoRegister().

go
func AutoRegister(name string, s Seeder)
ParameterTypeDescription
namestringUnique name for this seeder
sSeederPointer to a struct implementing the Seeder interface

Panics if the name is a duplicate or empty/whitespace-only — fail-fast at startup.

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 migrator.Run(), auto-registered seeders are discovered automatically. Just blank-import the seeders package in your main.go.

See Running Seeders.


DependentSeeder Interface

Optionally implement DependentSeeder to declare that a seeder must run after other seeders.

go
type DependentSeeder interface {
    Seeder
    DependsOn() []string
}
MethodSignatureDescription
DependsOnDependsOn() []stringReturns names of seeders that must run first

The runner performs a topological sort on the dependency graph to determine execution order.

go
type PostSeeder struct{}

func (s *PostSeeder) DependsOn() []string {
    return []string{"UserSeeder"}
}

func (s *PostSeeder) Run(db *sql.DB) error {
    _, err := db.Exec(
        "INSERT INTO posts (user_id, title) VALUES ($1, $2)",
        1, "First Post",
    )
    return err
}

Circular dependencies (A depends on B, B depends on A) cause a runtime error. Design seeders as a directed acyclic graph.

See Dependencies.


TaggedSeeder Interface

New in v1.0.0

Optionally implement TaggedSeeder to assign tags to a seeder for selective execution.

go
type TaggedSeeder interface {
    Seeder
    Tags() []string
}
MethodSignatureDescription
TagsTags() []stringReturns the tags this seeder belongs to

A seeder can have multiple tags. Only seeders implementing this interface are considered by RunByTag.

go
type DevUserSeeder struct{}

func (s *DevUserSeeder) Tags() []string {
    return []string{"development", "testing"}
}

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

See Seeder Tags.


RollbackableSeeder Interface

New in v1.0.0

Optionally implement RollbackableSeeder to define custom rollback logic for a seeder.

go
type RollbackableSeeder interface {
    Seeder
    Rollback(db *sql.DB) error
}
MethodSignatureDescription
RollbackRollback(db *sql.DB) errorUndoes the data inserted by Run
go
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
}

func (s *UserSeeder) Rollback(db *sql.DB) error {
    _, err := db.Exec("DELETE FROM users WHERE email = $1", "alice@example.com")
    return err
}

runner.Rollback() returns an error if the seeder does not implement RollbackableSeeder.

See Seeder Rollback.


seeder.NewRegistry()

Creates an empty seeder registry. Seeders are registered on the registry, which is then handed to a Runner.

go
func NewRegistry() *Registry

Returns: *Registry

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

reg := seeder.NewRegistry()

registry.Register()

Registers a seeder with the registry under a unique name. Names must be non-empty and unique.

go
func (r *Registry) Register(name string, s Seeder) error
ParameterTypeDescription
namestringUnique, non-empty name for this seeder
sSeederPointer to a struct implementing the Seeder interface

Returns: error — wraps ErrInvalidSeederName if the name is empty/whitespace, or ErrDuplicateSeeder if the name is already registered.

go
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)
}

The name passed to Register() is used to reference the seeder in Run() and in DependsOn() declarations.


registry.Get()

Retrieves a registered seeder by name.

go
func (r *Registry) Get(name string) (Seeder, error)
ParameterTypeDescription
namestringThe name used in Register()

Returns: (Seeder, error) — returns the seeder, or ErrSeederNotFound if no seeder is registered under that name.


registry.GetAll()

Returns a copy of all registered seeders keyed by name.

go
func (r *Registry) GetAll() map[string]Seeder

Returns: map[string]Seeder — a defensive copy of the registry contents.


seeder.NewRunner()

Creates a new seeder runner from a registry and a database connection.

go
func NewRunner(registry *Registry, db *sql.DB, logger Logger) *Runner
ParameterTypeDescription
registry*RegistryThe registry holding the seeders to run
db*sql.DBThe database connection for seeders to use
loggerLoggerOptional logger. May be nil, in which case logging is silently skipped

Returns: *Runner

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

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

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

The Logger interface is minimal:

go
type Logger interface {
    Info(msg string, args ...any)
    Error(msg string, args ...any)
}

runner.RunAll()

Executes all registered seeders. Respects dependency ordering.

go
func (r *Runner) RunAll() error

Returns: error — returns nil on success, or the first error encountered.

Seeders run sequentially. If a seeder returns an error, execution stops immediately. Seeders that completed before the failure remain applied.

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

See Running Seeders.


runner.Run()

Executes a single seeder by its registered name.

go
func (r *Runner) Run(name string) error
ParameterTypeDescription
namestringThe name used in Register()

Returns: error — returns an error if the name doesn't match any registered seeder or if the seeder fails.

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

See Running Seeders.


runner.RunByTag()

New in v1.0.0

Executes all registered seeders that implement TaggedSeeder and have a matching tag.

go
func (r *Runner) RunByTag(tag string) error
ParameterTypeDescription
tagstringThe tag to match against

Returns: error — returns nil on success, or the first error encountered.

Seeders that don't implement TaggedSeeder are silently skipped. Only seeders whose Tags() method returns a slice containing the specified tag are executed.

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

See Seeder Tags.


runner.Rollback()

New in v1.0.0

Rolls back a specific seeder by calling its Rollback method.

go
func (r *Runner) Rollback(name string) error
ParameterTypeDescription
namestringThe name used in Register()

Returns: error — returns an error if the name doesn't match any registered seeder, if the seeder doesn't implement RollbackableSeeder, or if the rollback fails.

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

See Seeder Rollback.


runner.Truncate()

New in v1.0.0

Deletes all rows from a table.

go
func (r *Runner) Truncate(table string) error
ParameterTypeDescription
tablestringThe table name to truncate

Returns: error — returns nil on success, or an error if the operation fails.

Uses DELETE FROM for broad compatibility across databases.

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

See Seeder Rollback.


seeder.CreateMany()

Inserts records in batches using multi-row INSERT statements. Defaults to DialectPostgres.

go
func CreateMany(db *sql.DB, table string, records []map[string]any, chunkSize int) error
ParameterTypeDescription
db*sql.DBThe database connection
tablestringTarget table name
records[]map[string]anySlice of records to insert
chunkSizeintRows per batch. Defaults to 500 if <= 0

Returns: error — returns nil on success, or an error with the batch index range on failure.

All records must have consistent keys. The function validates this before inserting.

go
records := []map[string]any{
    {"name": "Alice", "email": "alice@example.com"},
    {"name": "Bob", "email": "bob@example.com"},
}

if err := seeder.CreateMany(db, "users", records, 100); err != nil {
    log.Fatal(err)
}

See Batch Insert.


seeder.CreateManyWithDialect()

Inserts records in batches using dialect-specific SQL (placeholders and identifier quoting).

go
func CreateManyWithDialect(db *sql.DB, table string, records []map[string]any, chunkSize int, dialect Dialect) error
ParameterTypeDescription
db*sql.DBThe database connection
tablestringTarget table name
records[]map[string]anySlice of records to insert
chunkSizeintRows per batch. Defaults to 500 if <= 0
dialectDialectDatabase dialect (DialectPostgres, DialectMySQL, or DialectSQLite)

Returns: error — returns nil on success, or an error with the batch index range on failure.

go
records := []map[string]any{
    {"name": "Alice", "email": "alice@example.com"},
    {"name": "Bob", "email": "bob@example.com"},
}

// MySQL dialect — uses ? placeholders and `backtick` identifiers
if err := seeder.CreateManyWithDialect(db, "users", records, 100, seeder.DialectMySQL); err != nil {
    log.Fatal(err)
}

See Batch Insert.


Dialect Type

The Dialect type controls placeholder syntax and identifier quoting for batch inserts.

go
type Dialect int

const (
    DialectPostgres Dialect = iota // $1, $2 placeholders, "double quote" identifiers
    DialectMySQL                   // ?, ? placeholders, `backtick` identifiers
    DialectSQLite                  // ?, ? placeholders, "double quote" identifiers
)
ConstantPlaceholdersIdentifier Quoting
DialectPostgres$1, $2, $3"double quotes"
DialectMySQL?, ?, ?`backticks`
DialectSQLite?, ?, ?"double quotes"

See Batch Insert — Multi-Dialect Support.


Sentinel Errors

The seeder package exports sentinel errors you can match with errors.Is.

go
var (
    ErrDuplicateSeeder    = errors.New("duplicate seeder name")
    ErrInvalidSeederName  = errors.New("invalid seeder name")
    ErrSeederNotFound     = errors.New("seeder not found")
    ErrCircularDependency = errors.New("circular seeder dependency")
)
ErrorReturned byMeaning
ErrDuplicateSeederRegistry.RegisterA seeder is already registered under the given name
ErrInvalidSeederNameRegistry.RegisterThe name is empty or whitespace-only
ErrSeederNotFoundRegistry.Get, Runner.Run, Runner.Rollback, dependency resolutionNo seeder is registered under the given name
ErrCircularDependencyRunner.RunAll, Runner.Run, Runner.RunByTagThe dependency graph contains a cycle
go
if err := reg.Register("UserSeeder", &seeders.UserSeeder{}); errors.Is(err, seeder.ErrDuplicateSeeder) {
    // handle duplicate
}

Complete Example

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()
    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)

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

    log.Println("Seeding complete!")
}

Quick Reference

ComponentMethodSignatureDescription
InterfaceRunRun(db *sql.DB) errorExecute seeding logic
InterfaceDependsOnDependsOn() []stringDeclare dependencies (optional)
InterfaceTagsTags() []stringAssign tags (optional)
InterfaceRollbackRollback(db *sql.DB) errorCustom rollback logic (optional)
RegistryAutoRegisterAutoRegister(name, seeder)Register a seeder via init()
RegistryNewRegistryNewRegistry() *RegistryCreate a registry
RegistryRegisterRegister(name, seeder) errorRegister a seeder
RegistryGetGet(name) (Seeder, error)Look up a seeder by name
RegistryGetAllGetAll() map[string]SeederCopy of all registered seeders
RunnerNewRunnerNewRunner(registry, db, logger) *RunnerCreate a runner
RunnerRunAllRunAll() errorRun all seeders
RunnerRunRun(name) errorRun a specific seeder
RunnerRunByTagRunByTag(tag) errorRun seeders matching a tag
RunnerRollbackRollback(name) errorRoll back a specific seeder
RunnerTruncateTruncate(table) errorDelete all rows from a table
HelperCreateManyCreateMany(db, table, records, chunkSize) errorBatch insert (Postgres default)
HelperCreateManyWithDialectCreateManyWithDialect(db, table, records, chunkSize, dialect) errorBatch insert with dialect
TypeDialectDialectPostgres, DialectMySQL, DialectSQLiteDatabase dialect for batch inserts