| name | fake-data |
| description | Create a Go database seeder for a Goravel entity with 25+ realistic records. Covers all status/enum values, diverse data for sorting/pagination testing, and proper nullable field handling. |
| argument-hint | [EntityName] |
| allowed-tools | Bash, Read, Write, Edit, Grep, Glob |
Fake Data Seeder
Agent: Chikondi Banda — Senior Backend Engineer
Create a database seeder for $ARGUMENTS.
Prerequisites
- Model exists in
app/models/
- Migration has been run on dev DB (
go run . artisan migrate)
Step 1: Read the Model
Read app/models/<entity_name>.go to understand:
- All fields and their types
- Which fields are required vs nullable (
*string, *time.Time, etc.)
- Enum/status values (check request validators in
app/http/requests/)
- Relationships (FK fields)
Step 2: Create the Seeder File
Create database/seeders/<entity_name>_seeder.go:
package seeders
import (
"github.com/goravel/framework/facades"
"books-database/app/models"
"time"
)
type EntitySeeder struct {
}
func (s *EntitySeeder) Signature() string {
return "EntitySeeder"
}
func (s *EntitySeeder) Run() error {
var count int64
count, err := facades.Orm().Query().Model(&models.Entity{}).Count()
if err != nil {
return err
}
if count > 0 {
facades.Log().Infof("EntitySeeder: Skipping - table already has %d records", count)
return nil
}
entities := []models.Entity{
}
for _, entity := range entities {
if err := facades.Orm().Query().Create(&entity); err != nil {
return err
}
}
return nil
}
Data Generation Rules
Minimum Records: 25
Generate at least 25 records to ensure meaningful testing of:
- Pagination (default page size is typically 10-20)
- Sorting (needs diverse values to verify order)
- Search (needs varied text to test matching)
- Filters (needs records in each status/category)
Status/Enum Distribution
Distribute records across all enum values:
// For a 2-value enum (ACTIVE/INACTIVE) with 30 records:
// ~20 ACTIVE, ~10 INACTIVE
// For a 4-value enum (AVAILABLE/BORROWED/MAINTENANCE/RESERVED) with 50 records:
// ~20 AVAILABLE, ~12 BORROWED, ~10 MAINTENANCE, ~8 RESERVED
Every enum value must have at least 3 records so filter tabs show meaningful results.
Data Diversity for Sorting
Ensure fields have diverse values that produce a clear sort order:
{FirstName: "Alice", ...},
{FirstName: "Zara", ...},
{FirstName: "Michael", ...},
{FirstName: "John", ...},
{FirstName: "John", ...},
{FirstName: "Jane", ...},
For date fields, spread across multiple years:
parseDate("2020-03-15"),
parseDate("2022-07-22"),
parseDate("2024-01-10"),
For numeric fields (price, amount), use varied values:
Price: 9.99,
Price: 24.50,
Price: 149.99,
Nullable Fields
Mix nil and populated values for optional fields:
{Bio: strPtr("A detailed biography..."), Email: strPtr("alice@example.com")},
{Bio: nil, Email: strPtr("bob@example.com")},
{Bio: strPtr("Another bio"), Email: nil},
{Bio: nil, Email: nil},
Helper Functions
func strPtr(s string) *string { return &s }
func parseDate(dateStr string) *time.Time {
if dateStr == "" {
return nil
}
t, err := time.Parse("2006-01-02", dateStr)
if err != nil {
return nil
}
return &t
}
Important: Check if parseDate or strPtr already exist in another seeder file before redefining them (Go won't allow duplicate function names in the same package).
FK Relationships
If the entity references another entity via FK:
{AuthorID: uintPtr(1), ...},
{AuthorID: uintPtr(2), ...},
{AuthorID: nil, ...},
var authors []models.Author
facades.Orm().Query().Find(&authors)
Realistic Data
Use contextually appropriate data, not "Test Entity 1":
| Entity Type | Good Data | Bad Data |
|---|
| Author | "Chinua Achebe", "Chimamanda Adichie" | "Author 1", "Test Author" |
| Book | "Things Fall Apart", "Half of a Yellow Sun" | "Book 1", "Test Book" |
| Product | "Wireless Mouse", "Standing Desk" | "Product A", "Item 1" |
Group records by category/theme with comments for readability:
entities := []models.Entity{
{Name: "...", ...},
{Name: "...", ...},
{Name: "...", ...},
{Name: "...", ...},
}
Step 3: Register in DatabaseSeeder
Edit database/seeders/database_seeder.go to include the new seeder:
func (s *DatabaseSeeder) Run() error {
entitySeeder := &EntitySeeder{}
if err := entitySeeder.Run(); err != nil {
return err
}
return nil
}
Step 4: Register in kernel.go
Edit database/kernel.go to add the seeder:
func (kernel Kernel) Seeders() []seeder.Seeder {
return []seeder.Seeder{
&seeders.DatabaseSeeder{},
&seeders.BookSeeder{},
&seeders.RBACSeeder{},
&seeders.ConfigSeeder{},
&seeders.EntitySeeder{},
}
}
Step 5: Run the Seeder
go run . artisan db:seed
go run . artisan db:seed --seeder=EntitySeeder
Verify
go build ./...
go run . artisan db:seed --seeder=EntitySeeder
Idempotency (CRITICAL)
The seeder MUST be idempotent — running it multiple times should not create duplicate records.
Two patterns:
Pattern A: Count Check (simple, used by ConfigSeeder)
var count int64
count, _ = facades.Orm().Query().Model(&models.Entity{}).Count()
if count > 0 {
return nil
}
Pattern B: Upsert by Unique Field (for re-seeding)
for _, entity := range entities {
var existing models.Entity
err := facades.Orm().Query().Where("email = ?", entity.Email).First(&existing)
if err != nil {
facades.Orm().Query().Create(&entity)
}
}
Prefer Pattern A for simplicity. Use Pattern B only if you need to update existing seed data.
Data Checklist
Before finishing, verify:
Reference
- Book seeder:
database/seeders/book_seeder.go (50 records, grouped by genre)
- Config seeder:
database/seeders/config_seeder.go (idempotent with count check)
- Database seeder:
database/seeders/database_seeder.go (orchestrator)
- Kernel registration:
database/kernel.go