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:
type Seeder interface {
Run(db *sql.DB) error
}| Method | Signature | Description |
|---|---|---|
Run | Run(db *sql.DB) error | Executes the seeding logic |
The Run method receives a *sql.DB connection and returns an error if seeding fails.
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().
func AutoRegister(name string, s Seeder)| Parameter | Type | Description |
|---|---|---|
name | string | Unique name for this seeder |
s | Seeder | Pointer to a struct implementing the Seeder interface |
Panics if the name is a duplicate or empty/whitespace-only — fail-fast at startup.
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.
type DependentSeeder interface {
Seeder
DependsOn() []string
}| Method | Signature | Description |
|---|---|---|
DependsOn | DependsOn() []string | Returns names of seeders that must run first |
The runner performs a topological sort on the dependency graph to determine execution order.
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.
type TaggedSeeder interface {
Seeder
Tags() []string
}| Method | Signature | Description |
|---|---|---|
Tags | Tags() []string | Returns the tags this seeder belongs to |
A seeder can have multiple tags. Only seeders implementing this interface are considered by RunByTag.
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.
type RollbackableSeeder interface {
Seeder
Rollback(db *sql.DB) error
}| Method | Signature | Description |
|---|---|---|
Rollback | Rollback(db *sql.DB) error | Undoes the data inserted by Run |
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.
func NewRegistry() *RegistryReturns: *Registry
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.
func (r *Registry) Register(name string, s Seeder) error| Parameter | Type | Description |
|---|---|---|
name | string | Unique, non-empty name for this seeder |
s | Seeder | Pointer 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.
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.
func (r *Registry) Get(name string) (Seeder, error)| Parameter | Type | Description |
|---|---|---|
name | string | The 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.
func (r *Registry) GetAll() map[string]SeederReturns: map[string]Seeder — a defensive copy of the registry contents.
seeder.NewRunner()
Creates a new seeder runner from a registry and a database connection.
func NewRunner(registry *Registry, db *sql.DB, logger Logger) *Runner| Parameter | Type | Description |
|---|---|---|
registry | *Registry | The registry holding the seeders to run |
db | *sql.DB | The database connection for seeders to use |
logger | Logger | Optional logger. May be nil, in which case logging is silently skipped |
Returns: *Runner
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:
type Logger interface {
Info(msg string, args ...any)
Error(msg string, args ...any)
}runner.RunAll()
Executes all registered seeders. Respects dependency ordering.
func (r *Runner) RunAll() errorReturns: 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.
if err := runner.RunAll(); err != nil {
log.Fatal(err)
}See Running Seeders.
runner.Run()
Executes a single seeder by its registered name.
func (r *Runner) Run(name string) error| Parameter | Type | Description |
|---|---|---|
name | string | The name used in Register() |
Returns: error — returns an error if the name doesn't match any registered seeder or if the seeder fails.
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.
func (r *Runner) RunByTag(tag string) error| Parameter | Type | Description |
|---|---|---|
tag | string | The 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.
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.
func (r *Runner) Rollback(name string) error| Parameter | Type | Description |
|---|---|---|
name | string | The 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.
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.
func (r *Runner) Truncate(table string) error| Parameter | Type | Description |
|---|---|---|
table | string | The 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.
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.
func CreateMany(db *sql.DB, table string, records []map[string]any, chunkSize int) error| Parameter | Type | Description |
|---|---|---|
db | *sql.DB | The database connection |
table | string | Target table name |
records | []map[string]any | Slice of records to insert |
chunkSize | int | Rows 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.
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).
func CreateManyWithDialect(db *sql.DB, table string, records []map[string]any, chunkSize int, dialect Dialect) error| Parameter | Type | Description |
|---|---|---|
db | *sql.DB | The database connection |
table | string | Target table name |
records | []map[string]any | Slice of records to insert |
chunkSize | int | Rows per batch. Defaults to 500 if <= 0 |
dialect | Dialect | Database dialect (DialectPostgres, DialectMySQL, or DialectSQLite) |
Returns: error — returns nil on success, or an error with the batch index range on failure.
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.
type Dialect int
const (
DialectPostgres Dialect = iota // $1, $2 placeholders, "double quote" identifiers
DialectMySQL // ?, ? placeholders, `backtick` identifiers
DialectSQLite // ?, ? placeholders, "double quote" identifiers
)| Constant | Placeholders | Identifier 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.
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")
)| Error | Returned by | Meaning |
|---|---|---|
ErrDuplicateSeeder | Registry.Register | A seeder is already registered under the given name |
ErrInvalidSeederName | Registry.Register | The name is empty or whitespace-only |
ErrSeederNotFound | Registry.Get, Runner.Run, Runner.Rollback, dependency resolution | No seeder is registered under the given name |
ErrCircularDependency | Runner.RunAll, Runner.Run, Runner.RunByTag | The dependency graph contains a cycle |
if err := reg.Register("UserSeeder", &seeders.UserSeeder{}); errors.Is(err, seeder.ErrDuplicateSeeder) {
// handle duplicate
}Complete Example
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
| Component | Method | Signature | Description |
|---|---|---|---|
| Interface | Run | Run(db *sql.DB) error | Execute seeding logic |
| Interface | DependsOn | DependsOn() []string | Declare dependencies (optional) |
| Interface | Tags | Tags() []string | Assign tags (optional) |
| Interface | Rollback | Rollback(db *sql.DB) error | Custom rollback logic (optional) |
| Registry | AutoRegister | AutoRegister(name, seeder) | Register a seeder via init() |
| Registry | NewRegistry | NewRegistry() *Registry | Create a registry |
| Registry | Register | Register(name, seeder) error | Register a seeder |
| Registry | Get | Get(name) (Seeder, error) | Look up a seeder by name |
| Registry | GetAll | GetAll() map[string]Seeder | Copy of all registered seeders |
| Runner | NewRunner | NewRunner(registry, db, logger) *Runner | Create a runner |
| Runner | RunAll | RunAll() error | Run all seeders |
| Runner | Run | Run(name) error | Run a specific seeder |
| Runner | RunByTag | RunByTag(tag) error | Run seeders matching a tag |
| Runner | Rollback | Rollback(name) error | Roll back a specific seeder |
| Runner | Truncate | Truncate(table) error | Delete all rows from a table |
| Helper | CreateMany | CreateMany(db, table, records, chunkSize) error | Batch insert (Postgres default) |
| Helper | CreateManyWithDialect | CreateManyWithDialect(db, table, records, chunkSize, dialect) error | Batch insert with dialect |
| Type | Dialect | DialectPostgres, DialectMySQL, DialectSQLite | Database dialect for batch inserts |
Schema Builder
Complete package reference for the Schema Builder, Blueprint, and Grammar — all public methods with signatures, parameters, return types, and usage examples.
Factory
Complete package reference for the go-migration Factory[T] generic type and Faker methods — all public methods with signatures, parameters, return types, and usage examples.