|
| 1 | +# Database Seeders Guide |
| 2 | + |
| 3 | +This document explains how to create and register new database seeders in the IMS PocketBase BaaS Starter. |
| 4 | + |
| 5 | +## Overview |
| 6 | + |
| 7 | +The database seeder system allows you to populate your database with test data or initial data for development and production environments. Seeders are organized into two categories: |
| 8 | + |
| 9 | +1. **Migration Seeders** - Run automatically during database migrations (for essential data) |
| 10 | +2. **CLI Seeders** - Run manually via CLI commands (for test data and development) |
| 11 | + |
| 12 | +This guide focuses on **CLI Seeders** which are designed for development and testing purposes. |
| 13 | + |
| 14 | +## CLI Seeder Architecture |
| 15 | + |
| 16 | +CLI Seeders follow a simple manual registration pattern: |
| 17 | + |
| 18 | +- **Seeder Functions** - Individual functions that perform the seeding logic |
| 19 | +- **Registry** - Manual list of seeder functions in `internal/database/seeders/cli_seeder_registry.go` |
| 20 | +- **CLI Commands** - Commands to run individual or all seeders |
| 21 | + |
| 22 | +## Creating a New CLI Seeder |
| 23 | + |
| 24 | +### Step 1: Create the Seeder Function |
| 25 | + |
| 26 | +Add your seeder function to an existing file (like `user_seeder.go`) or create a new seeder file: |
| 27 | + |
| 28 | +```go |
| 29 | +// internal/database/seeders/user_seeder.go |
| 30 | + |
| 31 | +// SeedUsersCLI seeds a specified number of fake users |
| 32 | +func SeedUsersCLI(app core.App, count int) error { |
| 33 | + log := logger.FromApp(app) |
| 34 | + |
| 35 | + fmt.Printf("🌱 Seeding %d users...\n", count) |
| 36 | + |
| 37 | + log.Info("Seeding users", "count", count) |
| 38 | + |
| 39 | + userFactory := factories.NewUserFactory(app) |
| 40 | + |
| 41 | + users, err := userFactory.GenerateMany(count) |
| 42 | + if err != nil { |
| 43 | + return fmt.Errorf("failed to generate users: %w", err) |
| 44 | + } |
| 45 | + |
| 46 | + for i, user := range users { |
| 47 | + if err := app.Save(user); err != nil { |
| 48 | + return fmt.Errorf("failed to save user %d: %w", i+1, err) |
| 49 | + } |
| 50 | + log.Info("Created user", "name", user.GetString("name"), "email", user.GetString("email")) |
| 51 | + } |
| 52 | + |
| 53 | + fmt.Printf("✅ Successfully seeded %d users\n", count) |
| 54 | + |
| 55 | + log.Info("Successfully seeded users", "count", count) |
| 56 | + return nil |
| 57 | +} |
| 58 | +``` |
| 59 | + |
| 60 | +### Step 2: Register the Seeder Function |
| 61 | + |
| 62 | +Add your seeder function to the registry in `internal/database/seeders/cli_seeder_registry.go`: |
| 63 | + |
| 64 | +```go |
| 65 | +// GetAllCLISeederFunctions returns a list of all CLI seeder functions |
| 66 | +func GetAllCLISeederFunctions() []CLISeederFunction { |
| 67 | + return []CLISeederFunction{ |
| 68 | + // Existing seeders... |
| 69 | + { |
| 70 | + Name: "UserSeeder[10]", |
| 71 | + Description: "Seeds 10 fake users", |
| 72 | + Function: func(app core.App) error { |
| 73 | + return SeedUsersCLI(app, 10) |
| 74 | + }, |
| 75 | + }, |
| 76 | + // Add your new seeder here: |
| 77 | + { |
| 78 | + Name: "CustomSeeder[50]", |
| 79 | + Description: "Seeds 50 custom records", |
| 80 | + Function: func(app core.App) error { |
| 81 | + return SeedCustomRecords(app, 50) |
| 82 | + }, |
| 83 | + }, |
| 84 | + } |
| 85 | +} |
| 86 | +``` |
| 87 | + |
| 88 | +### Step 3: (Optional) Create a Dedicated CLI Command |
| 89 | + |
| 90 | +If you want to run your seeder individually, create a command handler in `internal/handlers/command/`: |
| 91 | + |
| 92 | +```go |
| 93 | +// internal/handlers/command/custom_seeder_command.go |
| 94 | + |
| 95 | +// HandleCustomSeederCommand handles the 'seed-custom' CLI command |
| 96 | +func HandleCustomSeederCommand(app *pocketbase.PocketBase, cmd *cobra.Command, args []string) { |
| 97 | + log := logger.GetLogger(app) |
| 98 | + |
| 99 | + log.Info("Starting custom seeder process") |
| 100 | + fmt.Println("🌱 Starting custom seeder process...") |
| 101 | + |
| 102 | + // Call the seeder function |
| 103 | + if err := seeders.SeedCustomRecords(app, 50); err != nil { |
| 104 | + log.Error("Failed to seed custom records", "error", err) |
| 105 | + fmt.Printf("❌ Error seeding custom records: %v\n", err) |
| 106 | + return |
| 107 | + } |
| 108 | + |
| 109 | + log.Info("Custom seeder process completed successfully") |
| 110 | + fmt.Println("✅ Custom seeder completed successfully") |
| 111 | +} |
| 112 | +``` |
| 113 | + |
| 114 | +Then register it in `internal/commands/commands.go`. For detailed instructions on creating custom CLI commands, see the [CLI Commands Guide](cli-commands.md). |
| 115 | + |
| 116 | +## Running Seeders |
| 117 | + |
| 118 | +### Run All Registered Seeders |
| 119 | + |
| 120 | +```bash |
| 121 | +./main db-seed |
| 122 | +``` |
| 123 | + |
| 124 | +This command runs all functions registered in `GetAllCLISeederFunctions()`. |
| 125 | + |
| 126 | +### Run Individual Seeders |
| 127 | + |
| 128 | +If you created a dedicated command: |
| 129 | + |
| 130 | +```bash |
| 131 | +./main seed-custom |
| 132 | +``` |
| 133 | + |
| 134 | +## Best Practices |
| 135 | + |
| 136 | +1. **Use Descriptive Names**: Give your seeders clear, descriptive names |
| 137 | +2. **Provide Useful Descriptions**: Help users understand what each seeder does |
| 138 | +3. **Handle Errors Gracefully**: Return meaningful error messages |
| 139 | +4. **Use the Logger**: Log important events with `logger.FromApp(app)` |
| 140 | +5. **Console Output**: Provide clear console feedback with emojis and formatting |
| 141 | +6. **Make Seeders Idempotent**: Design seeders so they can be run multiple times safely |
| 142 | +7. **Use Factories**: Leverage existing factories for generating fake data |
| 143 | + |
| 144 | +## Example: Complete Custom Seeder |
| 145 | + |
| 146 | +Here's a complete example of a custom seeder: |
| 147 | + |
| 148 | +```go |
| 149 | +// internal/database/seeders/custom_seeder.go |
| 150 | + |
| 151 | +// SeedProductsCLI seeds a specified number of fake products |
| 152 | +func SeedProductsCLI(app core.App, count int) error { |
| 153 | + log := logger.FromApp(app) |
| 154 | + |
| 155 | + fmt.Printf("🌱 Seeding %d products...\n", count) |
| 156 | + |
| 157 | + log.Info("Seeding products", "count", count) |
| 158 | + |
| 159 | + // Get products collection |
| 160 | + productsCollection, err := app.FindCollectionByNameOrId("products") |
| 161 | + if err != nil { |
| 162 | + return fmt.Errorf("failed to find products collection: %w", err) |
| 163 | + } |
| 164 | + |
| 165 | + // Generate fake products |
| 166 | + for i := 0; i < count; i++ { |
| 167 | + product := core.NewRecord(productsCollection) |
| 168 | + product.Set("name", faker.Word()) |
| 169 | + product.Set("price", faker.Price(10, 1000)) |
| 170 | + product.Set("description", faker.Sentence()) |
| 171 | + product.Set("in_stock", faker.Bool()) |
| 172 | + |
| 173 | + if err := app.Save(product); err != nil { |
| 174 | + return fmt.Errorf("failed to save product %d: %w", i+1, err) |
| 175 | + } |
| 176 | + |
| 177 | + log.Info("Created product", "name", product.GetString("name")) |
| 178 | + } |
| 179 | + |
| 180 | + fmt.Printf("✅ Successfully seeded %d products\n", count) |
| 181 | + |
| 182 | + log.Info("Successfully seeded products", "count", count) |
| 183 | + return nil |
| 184 | +} |
| 185 | +``` |
| 186 | + |
| 187 | +Register it in the registry: |
| 188 | + |
| 189 | +```go |
| 190 | +{ |
| 191 | + Name: "ProductSeeder[25]", |
| 192 | + Description: "Seeds 25 fake products", |
| 193 | + Function: func(app core.App) error { |
| 194 | + return SeedProductsCLI(app, 25) |
| 195 | + }, |
| 196 | +}, |
| 197 | +``` |
| 198 | + |
| 199 | +Now when you run `./main db-seed`, your new product seeder will be executed along with all other registered seeders. |
0 commit comments