Model factories and database seeders for NestJS with TypeORM.
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();- Installation
- Quick Start
- Module Configuration
- Defining Factories
- Using the Factory Builder
- States
- Sequences
- Seeders
- Events
- Using the Service Directly
- Configuration Options
- Testing
- Changelog
- Contributing
- Security
- Credits
- License
Install the package via npm:
npm install @nestbolt/factoryOr via yarn:
yarn add @nestbolt/factoryOr via pnpm:
pnpm add @nestbolt/factoryThis 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
@faker-js/faker ^9.0.0 # Bundled — no need to install separately
npm install @nestjs/event-emitter # For seeder lifecycle eventsimport{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"};}}import{FactoryModule}from"@nestbolt/factory";
@Module({imports: [TypeOrmModule.forRoot({/* ... */}),FactoryModule.forRoot({factories: [UserFactory,PostFactory],}),],})exportclassAppModule{}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();}}The module is registered globally — you only need to import it once.
FactoryModule.forRoot({factories: [UserFactory,PostFactory],seeders: [DatabaseSeeder],seed: 12345,// optional: reproducible faker data});FactoryModule.forRootAsync({imports: [ConfigModule],inject: [ConfigService],useFactory: (config: ConfigService)=>({factories: [UserFactory,PostFactory],seed: config.get("FAKER_SEED"),}),});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}}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();| 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 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();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 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);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`);}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();}}| 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 |
| Option | Type | Default | Description |
|---|---|---|---|
factories | FactoryClass[] | [] | Factory classes to register |
seeders | SeederClass[] | [] | Seeder classes to register |
seed | number | undefined | Faker seed for reproducible data |
npm testRun tests in watch mode:
npm run test:watchGenerate coverage report:
npm run test:covPlease see CHANGELOG for more information on what has changed recently.
Please see CONTRIBUTING for details.
If you discover any security-related issues, please report them via GitHub Issues with the security label instead of using the public issue tracker.
The MIT License (MIT). Please see License File for more information.