Seeding
Introduction
Seeders fill tables with starter or demo data after migrations have built the schema. Classes live in database/seeders. A default DatabaseSeeder can call other seeders so you control order without one giant file.
You insert with models, the query builder (DB from @bunyad/database), or factories from @bunyad/orm. Connection and query details live under database. Run seeders through the console (bunyad db:seed) or after migrate:fresh --seed.
Writing seeders
bunyad make:seeder UserSeederCreates database/seeders/UserSeeder.ts. The class name always ends in Seeder. --force overwrites an existing file.
A seeder extends Seeder from @bunyad/database and implements run:
import { Seeder } from "@bunyad/database";
import { Hash } from "@bunyad/auth";
import User from "@/Models/User.ts";
export default class UserSeeder extends Seeder {
async run(): Promise<void> {
await User.firstOrCreate(
{ email: "admin@example.com" },
{
name: "Admin",
password: await Hash.make("secret"),
},
);
}
}The class must be the default export. db:seed imports database/seeders/<Name>.ts and constructs that export.
Query builder inserts
When you do not need a model:
import { DB, Seeder } from "@bunyad/database";
import { Hash } from "@bunyad/auth";
export default class UserSeeder extends Seeder {
async run(): Promise<void> {
await DB.table("users").insert({
name: "Ada",
email: "ada@example.com",
password: await Hash.make("secret"),
});
}
}Using model factories
Factories live under database/factories. Generate one with bunyad make:factory UserFactory, then bind it on the model with @HasFactory:
import { HasFactory, Model } from "@bunyad/orm";
import UserFactory from "../../database/factories/UserFactory.ts";
@HasFactory(UserFactory)
export default class User extends Model {
// ...
}In a seeder:
import { Seeder } from "@bunyad/database";
import User from "@/Models/User.ts";
export default class UserSeeder extends Seeder {
async run(): Promise<void> {
await User.factory().count(50).create();
}
}make builds in-memory models. create persists them. Pass attribute overrides to either method.
Calling additional seeders
call runs another seeder class:
import { Seeder } from "@bunyad/database";
import UserSeeder from "./UserSeeder.ts";
import PostSeeder from "./PostSeeder.ts";
export default class DatabaseSeeder extends Seeder {
async run(): Promise<void> {
await this.call(UserSeeder);
await this.call(PostSeeder);
}
}call constructs the class and awaits run. Order is the order of your await this.call(...) lines.
Running seeders
bunyad db:seedBoots the application, loads database/seeders/DatabaseSeeder.ts, and runs it. On success it prints Database seeded: DatabaseSeeder.
Run a specific class:
bunyad db:seed --class UserSeederThe value after --class is the filename without .ts (for example UserSeeder → database/seeders/UserSeeder.ts).
Fresh migrate and seed
bunyad migrate:fresh --seedDrops all tables, re-runs migrations, then runs DatabaseSeeder. There is no separate --seeder= flag on migrate:fresh; seed a different class with db:seed --class afterward.
bunyad db:wipe --seedWipes tables, then runs migrate and db:seed.
Seeders are not prompted for confirmation in production. Treat db:seed, migrate:fresh --seed, and db:wipe --seed as data-replacing commands and run them only where that is intended.