Factory#

Type-safe data factories for generating test and seed data. Factories provide a clean, reusable way to create test data with sensible defaults and easy overrides.

Installation#

@faker-js/faker is an optional peer dependency for generating realistic fake data:

typescript
bun add -d @faker-js/faker

Quick Start#

typescript
bunx buntok make:factory user

The CLI creates a Prisma-oriented scaffold. Replace its placeholder definition with the fields required by your model before compiling it.

typescript
// src/factories/user.factory.ts
import { Factory } from "@buntok/core";
import { faker } from "@faker-js/faker";
import type { User } from "@prisma/client";

export const UserFactory = Factory.define<User>(() => ({
  id: faker.number.int({ max: 10000 }),
  name: faker.person.fullName(),
  email: faker.internet.email(),
  role: faker.helpers.arrayElement(["user", "admin"]),
}));

Basic Usage#

typescript
import { UserFactory } from "@/factories/user.factory";

// Create one item
const user = await UserFactory.create();
// → { id: 1234, name: "John Doe", email: "john@example.com", role: "user" }

// Create many items
const users = await UserFactory.createMany(10);
// → array of 10 users

// Override fields per-call
const admin = await UserFactory.create({ role: "admin" });
// → { id: 5678, name: "Jane Smith", email: "jane@example.com", role: "admin" }

Sync Build#

Use build() for synchronous generation without the afterCreate hook:

typescript
// Synchronous (no afterCreate hook)
const user = UserFactory.build({ name: "John" });
const users = UserFactory.buildMany(5);

Default Overrides#

Set defaults that apply to all created items:

typescript
const UserFactory = Factory.define<User>(() => ({
  id: faker.number.int({ max: 10000 }),
  name: faker.person.fullName(),
  email: faker.internet.email(),
  role: faker.helpers.arrayElement(["user", "admin"]),
})).withDefaults({ role: "user" });

// All created items will have role: "user" unless overridden
const user = await UserFactory.create();  // role: "user"
const admin = await UserFactory.create({ role: "admin" });  // role: "admin"

After Create Hook#

Run custom logic after each item is created (useful for database inserts, audit logs, etc.):

typescript
const UserFactory = Factory.define<User>(() => ({
  id: faker.number.int({ max: 10000 }),
  name: faker.person.fullName(),
  email: faker.internet.email(),
  role: faker.helpers.arrayElement(["user", "admin"]),
})).afterCreate(async (user) => {
  await db.auditLog.create({
    data: { userId: user.id, action: "created" }
  });
});

Relations#

Use Factory.ref() for lazy references to other factories:

typescript
const UserFactory = Factory.define<User>(() => ({
  id: faker.number.int(),
  name: faker.person.fullName(),
}));

const PostFactory = Factory.define(() => ({
  id: faker.number.int(),
  title: faker.lorem.sentence(),
  userId: Factory.ref(() => UserFactory.build().id),
}));

// Each post gets a fresh user ID
const posts = await PostFactory.createMany(5);

Random Pick#

typescript
// Pick one random item
const status = Factory.pick(["draft", "published", "archived"]);
// → "published"

// Pick multiple random items
const tags = Factory.pick(["typescript", "bun", "api", "testing"], 2);
// → ["api", "typescript"]

Using with Seeders#

Generate seeders that use factories with the --factory flag:

typescript
bunx buntok make:seeder user --factory
typescript
// src/db/seeders/user.seeder.ts
import { prisma } from "@/lib/prisma";
import { UserFactory } from "@/factories/user.factory";

export async function seedUser() {
  console.log("Seeding User...");

  const items = UserFactory.buildMany(100);
  await prisma.user.createMany({ data: items });

  console.log("✓ User seeded successfully (100 records)");
}

API Reference#

MethodDescription
Factory.define<T>(fn)Create a new factory with type T
.create(overrides?)Create one item (async, runs afterCreate)
.createMany(count, overrides?)Create multiple items
.build(overrides?)Generate one item synchronously (no afterCreate)
.buildMany(count, overrides?)Generate multiple items synchronously
.withDefaults(defaults)Set default overrides for all created items
.afterCreate(callback)Register post-creation hook (async supported)
Factory.ref(fn)Lazy reference for relations
Factory.pick(arr, count?)Random item(s) from array
Info
Factories are designed for test and seed data generation. For production data, use your ORM's create methods directly.