Skip to content

nestbolt/factory

Repository files navigation

@nestbolt/factory

Model factories and database seeders for NestJS with TypeORM.

npm version npm downloads tests license


This package provides model factories and database seeders for NestJS that let you generate fake data for any TypeORM entity with a fluent, chainable API.

Once installed, using it is as simple as:

class UserFactory extends BaseFactory<User> {
  get entity() { return User; }
  definition(faker: Faker) {
    return { name: faker.person.fullName(), email: faker.internet.email() };
  }
}

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

Table of Contents

Installation

Install the package via npm:

npm install @nestbolt/factory

Or via yarn:

yarn add @nestbolt/factory

Or via pnpm:

pnpm add @nestbolt/factory

Peer Dependencies

This package requires the following peer dependencies, which you likely already have in a NestJS project:

@nestjs/common      ^10.0.0 || ^11.0.0
@nestjs/core        ^10.0.0 || ^11.0.0
@nestjs/typeorm     ^10.0.0 || ^11.0.0
typeorm             ^0.3.0
reflect-metadata    ^0.1.13 || ^0.2.0

Included

@faker-js/faker     ^9.0.0   # Bundled — no need to install separately

Optional

npm install @nestjs/event-emitter   # For seeder lifecycle events

Quick Start

1. Define a factory

import { BaseFactory } from "@nestbolt/factory";
import { Faker } from "@faker-js/faker";

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",
    };
  }

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

2. Register the module

import { FactoryModule } from "@nestbolt/factory";

@Module({
  imports: [
    TypeOrmModule.forRoot({ /* ... */ }),
    FactoryModule.forRoot({
      factories: [UserFactory, PostFactory],
    }),
  ],
})
export class AppModule {}

3. Use in your code

import { FactoryService } from "@nestbolt/factory";

@Injectable()
export class SeedService {
  constructor(private readonly factory: FactoryService) {}

  async seed() {
    await this.factory.use(UserFactory).count(10).create();
    await this.factory.use(UserFactory).state("admin").create();
  }
}

Module Configuration

The module is registered globally — you only need to import it once.

Static Configuration (forRoot)

FactoryModule.forRoot({
  factories: [UserFactory, PostFactory],
  seeders: [DatabaseSeeder],
  seed: 12345,  // optional: reproducible faker data
});

Async Configuration (forRootAsync)

FactoryModule.forRootAsync({
  imports: [ConfigModule],
  inject: [ConfigService],
  useFactory: (config: ConfigService) => ({
    factories: [UserFactory, PostFactory],
    seed: config.get("FAKER_SEED"),
  }),
});

Defining Factories

Extend BaseFactory<T> and implement the entity getter and definition() method:

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",
    };
  }

  // Optional lifecycle hooks
  async afterMake(entity: Post, faker: Faker) {
    // Called after make() — entity is NOT persisted yet
  }

  async afterCreate(entity: Post, faker: Faker) {
    // Called after create() — entity IS persisted
  }
}

Using the Factory Builder

The FactoryBuilder provides a fluent API for generating entities:

// Make without persisting
const user = await factoryService.use(UserFactory).make();

// Create and persist to database
const user = await factoryService.use(UserFactory).create();

// Multiple entities
const users = await factoryService.use(UserFactory).count(10).create();

// Always get an array (even for count=1)
const users = await factoryService.use(UserFactory).createMany();
const users = await factoryService.use(UserFactory).makeMany();
Method Returns Description
count(n) this Set number of entities to generate
state(name | object | fn) this Apply a state (see States)
override(attrs) this Override specific fields (highest priority)
sequence(field, seq) this Apply a sequence to a field
afterCreating(fn) this Callback after persist
afterMaking(fn) this Callback after instantiation
create() T | T[] Persist and return
make() T | T[] Instantiate without persisting
createMany() T[] Persist and always return array
makeMany() T[] Instantiate and always return array

Override priority: definition()state()sequence()override() (highest)

States

States let you define named variations of a factory:

export class UserFactory extends BaseFactory<User> {
  // ... definition

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

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

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

// Object and function states also work
await factoryService.use(UserFactory).state({ role: "moderator" }).create();
await factoryService.use(UserFactory).state((faker) => ({ age: faker.number.int({ min: 18, max: 30 }) })).create();

Sequences

Use Sequence for auto-incrementing or cycling values:

import { Sequence } from "@nestbolt/factory";

// Auto-increment
await factoryService.use(UserFactory)
  .count(3)
  .sequence("email", Sequence.from(i => `user${i}@test.com`))
  .create();
// → user0@test.com, user1@test.com, user2@test.com

// Increment numbers
Sequence.increment()       // 1, 2, 3, ...
Sequence.increment(100)    // 100, 101, 102, ...

// Cycle through values
Sequence.cycle(["draft", "published", "archived"])
// → draft, published, archived, draft, ...

Seeders

Seeders are classes that populate your database with test data:

import { Seeder, FactoryService } from "@nestbolt/factory";

export class DatabaseSeeder implements Seeder {
  order = 0;  // lower runs first

  async run(factory: FactoryService): Promise<void> {
    await factory.use(UserFactory).count(10).create();
    await factory.use(UserFactory).state("admin").count(2).create();
    await factory.use(PostFactory).count(20).create();
  }
}

Register and run seeders:

// Register in module
FactoryModule.forRoot({
  factories: [UserFactory, PostFactory],
  seeders: [DatabaseSeeder],
});

// Run all seeders (sorted by order)
await factoryService.seed();

// Run a single seeder
await factoryService.runSeeder(DatabaseSeeder);

Events

When @nestjs/event-emitter is installed, the package emits:

Event Payload When
factory.seeder.started { seederClass } Before a seeder runs
factory.seeder.completed { seederClass } After a seeder completes
factory.seed.all.started { seederCount } Before seed() runs all seeders
factory.seed.all.completed { seederCount } After seed() completes all seeders
import { FACTORY_EVENTS, SeederCompletedEvent } from "@nestbolt/factory";
import { OnEvent } from "@nestjs/event-emitter";

@OnEvent(FACTORY_EVENTS.SEEDER_COMPLETED)
handleSeederCompleted(event: SeederCompletedEvent) {
  console.log(`Seeder ${event.seederClass} completed`);
}

Using the Service Directly

Inject FactoryService for factory and seeder management:

import { FactoryService } from "@nestbolt/factory";

@Injectable()
export class SeedService {
  constructor(private readonly factory: FactoryService) {}

  async seedDatabase() {
    const users = await this.factory.use(UserFactory).count(10).createMany();
    const faker = this.factory.getFaker();
    await this.factory.seed();
  }
}
Method Returns Description
use(FactoryClass) FactoryBuilder<T> Get builder for a registered factory
getFaker() Faker Get the configured Faker instance
seed() Promise<void> Run all registered seeders (sorted by order)
runSeeder(SeederClass) Promise<void> Run a single seeder
getOptions() FactoryModuleOptions Get module configuration

Configuration Options

Option Type Default Description
factories FactoryClass[] [] Factory classes to register
seeders SeederClass[] [] Seeder classes to register
seed number undefined Faker seed for reproducible data

Testing

npm test

Run tests in watch mode:

npm run test:watch

Generate coverage report:

npm run test:cov

Changelog

Please see CHANGELOG for more information on what has changed recently.

Contributing

Please see CONTRIBUTING for details.

Security

If you discover any security-related issues, please report them via GitHub Issues with the security label instead of using the public issue tracker.

License

The MIT License (MIT). Please see License File for more information.

About

Model factories and database seeders for NestJS with TypeORM.

Topics

Resources

License

Contributing

Stars

Watchers

Forks

Contributors