Backend

Seeders

Seeders fill your database with starter data — the admin account, demo users, sample catalogue rows, anything you want on a fresh install. In Grit, every resource gets its own seeder file, you can generate one in a single command, and --faker fills it with realistic rows (relationships included).

How it fits together

There is one thin Seed() runner that calls a Seed<Resource> function per resource. Each of those lives in its own file under internal/database/, so a seeder is always easy to find and edit — including the built-in users and blogs.

Seed() runnerPer-resource seedersDatabasecallsupsertSeed(db)the runnerSeedUsersbuilt-inSeedBlogsbuilt-inSeedProductyoursPostgreSQLidempotent upserts
RunnerBuilt-in seedersDatabase
One runner calls a Seed<Resource> per file — safe to run repeatedly
apps/api/internal/database/
seed.go ← Seed(db): the runner
├─ SeedUsers(db) → users_seeder.go (admin + demo users)
├─ SeedBlogs(db) → blogs_seeder.go (sample posts)
├─ SeedCategories(db) → categories_seeder.go
└─ SeedProducts(db) → products_seeder.go
└─ grit generate seeder / --seed adds these
seed_helpers.go ← pickID / firstID (relationship helpers)

When you generate a seeder, Grit writes the <resource>_seeder.go file and registers its call in seed.go at the // grit:seeders marker. You never wire anything by hand.

Running seeders

After migrating, run every seeder with one command from anywhere in the project:

Terminal
$grit seed

Seeders are idempotent — each checks whether its table already has rows and skips if so, so re-running never duplicates data.

output
Seeding database...
Created admin user: admin@example.com / admin123
Created user: jane@example.com / admin123
Created blog: "Getting Started with Grit" (published)
Seeded 8 category
Seeded 60 product
Database seeded successfully.

Generating a seeder

Add a seeder to a resource you already generated — it reads the model to pre-fill one example record with the right field types:

Terminal
$grit generate seeder Customer

Pass more than one, or emit the seeder at the same time you scaffold the resource with --seed:

Terminal
$grit generate seeder Customer Order Product
$grit generate resource Tag --fields "name:string" --seed

Filling rows with faker

Without a flag you get one editable example row. Add --faker (and --count N, default 10) to instead generate a loop that fills many rows with gofakeit. It ships inside the API, so this works offline.

Terminal
$grit generate seeder Product --faker --count 60

Values are chosen from each field's name and type:

FieldFaker value
namegofakeit.Name()
emailgofakeit.Email()
phone / city / companygofakeit.Phone() / City() / Company()
float (price)gofakeit.Price(1, 1000)
int / uintgofakeit.Number(1, 100)
boolgofakeit.Bool()
date / datetimegofakeit.Date()
file:image / files:imagea sample picsum image URL

Anything the guesser doesn't recognise falls back to gofakeit.Word(). It's just Go — open the file and swap in your own calls.

Relationships

This is the part most seeders get wrong. A belongs_to field (a Product's Category, say) needs a real parent id, not a random string. Grit handles it: the seeder loads the parent ids once and links each row to one of them — a random parent for faker, the first parent for the static example.

products_seeder.go (faker)
func SeedProducts(db *gorm.DB) error {
// ... skip if already seeded ...
// Link each row to an existing parent (loaded once).
var categoryIDs []string
db.Model(&models.Category{}).Pluck("id", &categoryIDs)
const n = 60
for i := 0; i < n; i++ {
r := models.Product{
Name: gofakeit.Name(),
Price: gofakeit.Price(1, 1000),
CategoryID: pickID(categoryIDs), // ← a real, existing category
}
db.Create(&r)
}
return nil
}

Seed order matters. A child can only link to a parent that already exists, so seed parents first. The runner calls seeders in the order you generated the resources — generate Category before Product and you're set. Need a different order? Reorder the calls in seed.go.

Editing a seeder

A static seeder is a plain slice of model structs — edit the values, add rows, done:

categories_seeder.go
func SeedCategories(db *gorm.DB) error {
var count int64
db.Model(&models.Category{}).Count(&count)
if count > 0 {
return nil // already seeded
}
records := []models.Category{
{Name: "Sample Name"}, // ← edit these
// {Name: "Phones"}, // ← or add your own
// {Name: "Accessories"},
}
for _, r := range records {
db.Create(&r)
}
return nil
}

Command reference

CommandDoes
grit seedRun every seeder
grit generate seeder X [Y…]Add a seeder to existing resource(s)
grit generate resource X … --seedEmit the seeder while scaffolding
… --faker --count NFill N rows with gofakeit instead of one example