NestboltNestbolt

@nestbolt/factory

Defining Factories

Create factory classes with entity definitions, state methods, and lifecycle hooks.

Factories are classes that define how to generate fake data for a specific TypeORM entity. Each factory extends BaseFactory<T> and provides a definition() method that returns default attributes.

Basic Factory

A minimal factory requires two things: an entity getter and a definition() method:

import { BaseFactory } from "@nestbolt/factory";
import type { Faker } from "@faker-js/faker";
import { User } from "../entities/user.entity";

export class UserFactory extends BaseFactory<User> {
  get entity() {
    return User;
  }

  definition(faker: Faker): Partial<User> {
    return {
      name: faker.person.fullName(),
      email: faker.internet.email(),
      role: "user",
      age: faker.number.int({ min: 18, max: 65 }),
      active: true,
    };
  }
}

entity

The entity getter returns the TypeORM entity class constructor. This is used to instantiate new entities and to resolve the TypeORM repository when persisting.

definition(faker)

The definition() method receives a Faker instance and returns a Partial<T> with default attribute values. This method is called once per entity generated.

You can use any Faker method to generate realistic data:

definition(faker: Faker): Partial<Post> {
  return {
    title: faker.lorem.sentence(),
    body: faker.lorem.paragraphs(3),
    status: faker.helpers.arrayElement(["draft", "published"]),
    viewCount: faker.number.int({ min: 0, max: 10000 }),
    publishedAt: faker.date.past(),
  };
}

State Methods

State methods define named variations of your factory. Any method on your factory class that returns a Partial<T> can be used as a named state:

export class UserFactory extends BaseFactory<User> {
  get entity() { return User; }

  definition(faker: Faker): Partial<User> {
    return {
      name: faker.person.fullName(),
      email: faker.internet.email(),
      role: "user",
      active: true,
    };
  }

  admin(): Partial<User> {
    return { role: "admin" };
  }

  inactive(): Partial<User> {
    return { active: false };
  }

  withAge(age: number): Partial<User> {
    return { age };
  }
}

States are applied via the builder's .state() method:

await factoryService.use(UserFactory).state("admin").create();
await factoryService.use(UserFactory).state("admin").state("inactive").create();

See States for more details on using states.

Lifecycle Hooks

Factories support two optional lifecycle hooks that run after an entity is generated:

afterMake

Called after an entity is instantiated but not yet persisted. Runs for both make() and create() calls:

export class UserFactory extends BaseFactory<User> {
  get entity() { return User; }

  definition(faker: Faker): Partial<User> {
    return {
      name: faker.person.fullName(),
      email: faker.internet.email(),
    };
  }

  async afterMake(entity: User, faker: Faker): Promise<void> {
    // Normalize email to lowercase
    entity.email = entity.email.toLowerCase();
  }
}

afterCreate

Called after an entity is persisted to the database. Only runs for create() and createMany() calls:

export class PostFactory extends BaseFactory<Post> {
  get entity() { return Post; }

  definition(faker: Faker): Partial<Post> {
    return {
      title: faker.lorem.sentence(),
      body: faker.lorem.paragraphs(2),
      status: "draft",
    };
  }

  async afterCreate(entity: Post, faker: Faker): Promise<void> {
    // Log or perform side effects after persistence
    console.log(`Created post #${entity.id}: ${entity.title}`);
  }
}

Hook Execution Order

When calling create(), hooks execute in this order:

  1. definition() generates base attributes
  2. States, sequences, and overrides are applied
  3. Entity is instantiated
  4. Factory afterMake runs (if defined)
  5. Builder afterMaking callbacks run (if any)
  6. Entity is persisted to the database
  7. Factory afterCreate runs (if defined)
  8. Builder afterCreating callbacks run (if any)

When calling make(), only steps 1--5 execute.

Factory Organization

A recommended project structure for factories:

src/
  factories/
    user.factory.ts
    post.factory.ts
    comment.factory.ts
  seeders/
    database.seeder.ts
  entities/
    user.entity.ts
    post.entity.ts
    comment.entity.ts

Registration

All factories must be registered in FactoryModule.forRoot() before they can be used:

FactoryModule.forRoot({
  factories: [UserFactory, PostFactory, CommentFactory],
});

Using an unregistered factory throws a FactoryNotRegisteredException:

// This throws if CommentFactory is not in the factories array
factoryService.use(CommentFactory);