Skip to content

Repository files navigation

@nestbolt/factory

Model factories and database seeders for NestJS with TypeORM.

npm versionnpm downloadstestslicense


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:

classUserFactoryextendsBaseFactory<User>{getentity(){returnUser;}definition(faker: Faker){return{name: faker.person.fullName(),email: faker.internet.email()};}}awaitfactoryService.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";exportclassUserFactoryextendsBaseFactory<User>{getentity(){returnUser;}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],}),],})exportclassAppModule{}

3. Use in your code

import{FactoryService}from"@nestbolt/factory";
@Injectable()exportclassSeedService{constructor(privatereadonlyfactory: FactoryService){}asyncseed(){awaitthis.factory.use(UserFactory).count(10).create();awaitthis.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:

exportclassPostFactoryextendsBaseFactory<Post>{getentity(){returnPost;}definition(faker: Faker): Partial<Post>{return{title: faker.lorem.sentence(),body: faker.lorem.paragraphs(2),status: "draft",};}// Optional lifecycle hooksasyncafterMake(entity: Post,faker: Faker){// Called after make() — entity is NOT persisted yet}asyncafterCreate(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 persistingconstuser=awaitfactoryService.use(UserFactory).make();// Create and persist to databaseconstuser=awaitfactoryService.use(UserFactory).create();// Multiple entitiesconstusers=awaitfactoryService.use(UserFactory).count(10).create();// Always get an array (even for count=1)constusers=awaitfactoryService.use(UserFactory).createMany();constusers=awaitfactoryService.use(UserFactory).makeMany();
MethodReturnsDescription
count(n)thisSet number of entities to generate
state(name | object | fn)thisApply a state (see States)
override(attrs)thisOverride specific fields (highest priority)
sequence(field, seq)thisApply a sequence to a field
afterCreating(fn)thisCallback after persist
afterMaking(fn)thisCallback 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:

exportclassUserFactoryextendsBaseFactory<User>{// ... definitionadmin(): Partial<User>{return{role: "admin"};}inactive(): Partial<User>{return{active: false};}}// UsageawaitfactoryService.use(UserFactory).state("admin").create();awaitfactoryService.use(UserFactory).state("admin").state("inactive").create();// Object and function states also workawaitfactoryService.use(UserFactory).state({role: "moderator"}).create();awaitfactoryService.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-incrementawaitfactoryService.use(UserFactory).count(3).sequence("email",Sequence.from(i=>`user${i}@test.com`)).create();// → user0@test.com, user1@test.com, user2@test.com// Increment numbersSequence.increment()// 1, 2, 3, ...Sequence.increment(100)// 100, 101, 102, ...// Cycle through valuesSequence.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";exportclassDatabaseSeederimplementsSeeder{order=0;// lower runs firstasyncrun(factory: FactoryService): Promise<void>{awaitfactory.use(UserFactory).count(10).create();awaitfactory.use(UserFactory).state("admin").count(2).create();awaitfactory.use(PostFactory).count(20).create();}}

Register and run seeders:

// Register in moduleFactoryModule.forRoot({factories: [UserFactory,PostFactory],seeders: [DatabaseSeeder],});// Run all seeders (sorted by order)awaitfactoryService.seed();// Run a single seederawaitfactoryService.runSeeder(DatabaseSeeder);

Events

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

EventPayloadWhen
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()exportclassSeedService{constructor(privatereadonlyfactory: FactoryService){}asyncseedDatabase(){constusers=awaitthis.factory.use(UserFactory).count(10).createMany();constfaker=this.factory.getFaker();awaitthis.factory.seed();}}
MethodReturnsDescription
use(FactoryClass)FactoryBuilder<T>Get builder for a registered factory
getFaker()FakerGet the configured Faker instance
seed()Promise<void>Run all registered seeders (sorted by order)
runSeeder(SeederClass)Promise<void>Run a single seeder
getOptions()FactoryModuleOptionsGet module configuration

Configuration Options

OptionTypeDefaultDescription
factoriesFactoryClass[][]Factory classes to register
seedersSeederClass[][]Seeder classes to register
seednumberundefinedFaker 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

Contributing

Stars

0 stars

Watchers

0 watching

Forks

Releases

Contributors

Languages