Skip to content

Mongoose ​

This guide walks you through seeding a MongoDB database end-to-end with Mongoose and @ackplus/nest-seeder: a schema, a factory, a seeder, and the config that wires it all together.

Install ​

bash
npm install @ackplus/nest-seeder @faker-js/faker
npm install @nestjs/mongoose mongoose

TIP

If you write your seeder.config.ts in TypeScript, also install the dev dependencies:

bash
npm install -D ts-node typescript

Define a schema ​

Use the standard NestJS @Schema() / @Prop() decorators and export the generated schema.

ts
// src/database/schemas/user.schema.ts
import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
import { HydratedDocument } from 'mongoose';

export type UserDocument = HydratedDocument<User>;

@Schema({ timestamps: true })
export class User {
  @Prop({ required: true })
  firstName: string;

  @Prop({ required: true })
  lastName: string;

  @Prop({ required: true, unique: true })
  email: string;

  @Prop({ default: 'user' })
  role: string;
}

export const UserSchema = SchemaFactory.createForClass(User);

Create a factory ​

A factory is a plain class whose properties are decorated with @Factory. Each generator receives a Faker instance, and a context object (ctx) carrying override values plus any already-generated fields it depends on.

ts
// src/database/factories/user.factory.ts
import { Factory } from '@ackplus/nest-seeder';

export class UserFactory {
  @Factory((faker) => faker.person.firstName())
  firstName: string;

  @Factory((faker) => faker.person.lastName())
  lastName: string;

  @Factory(
    (faker, ctx) => faker.internet.email({ firstName: ctx.firstName, lastName: ctx.lastName }).toLowerCase(),
    ['firstName', 'lastName'],
  )
  email: string;

  @Factory((faker) => faker.helpers.arrayElement(['admin', 'user', 'guest']))
  role: string;
}

Faker v9

Use faker.number.int({ min, max }) for numbers — faker.datatype.number was removed in Faker v9. faker.datatype.boolean() is still valid.

Write a seeder ​

Inject the Mongoose model with @InjectModel, generate documents with DataFactory, and persist them with insertMany(). The optional drop() method clears the collection with deleteMany({}).

ts
// src/database/seeders/user.seeder.ts
import { Injectable } from '@nestjs/common';
import { InjectModel } from '@nestjs/mongoose';
import { Model } from 'mongoose';
import { Seeder, SeederName, DataFactory } from '@ackplus/nest-seeder';
import { User, UserDocument } from '../schemas/user.schema';
import { UserFactory } from '../factories/user.factory';

@Injectable()
@SeederName('users')
export class UserSeeder implements Seeder {
  constructor(
    @InjectModel(User.name) private readonly userModel: Model<UserDocument>,
  ) {}

  async seed(): Promise<void> {
    const factory = DataFactory.createForClass(UserFactory);
    const users = factory.generate(10);
    await this.userModel.insertMany(users);
  }

  async drop(): Promise<void> {
    await this.userModel.deleteMany({});
  }
}

INFO

drop() is optional. When present, it runs before seed() on --refresh, making your seed runs idempotent.

Configure ​

Create a seeder.config.ts at your project root. Register the Mongoose connection with MongooseModule.forRoot(...) and the model with MongooseModule.forFeature([{ name: User.name, schema: UserSchema }]).

ts
// seeder.config.ts
import { MongooseModule } from '@nestjs/mongoose';
import { defineSeederConfig } from '@ackplus/nest-seeder';
import { User, UserSchema } from './src/database/schemas/user.schema';
import { UserSeeder } from './src/database/seeders/user.seeder';

export default defineSeederConfig({
  imports: [
    MongooseModule.forRoot(process.env.MONGO_URI ?? 'mongodb://localhost:27017/app'),
    MongooseModule.forFeature([{ name: User.name, schema: UserSchema }]),
  ],
  seeders: [UserSeeder],
});

TIP

defineSeederConfig is an identity helper — it only exists to give you full type-safety and autocompletion. It is optional but recommended.

Async config with ConfigService ​

If your connection string lives in environment variables managed by @nestjs/config, use MongooseModule.forRootAsync to inject the ConfigService.

ts
// seeder.config.ts
import { MongooseModule } from '@nestjs/mongoose';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { defineSeederConfig } from '@ackplus/nest-seeder';
import { User, UserSchema } from './src/database/schemas/user.schema';
import { UserSeeder } from './src/database/seeders/user.seeder';

export default defineSeederConfig({
  imports: [
    ConfigModule.forRoot({ isGlobal: true }),
    MongooseModule.forRootAsync({
      imports: [ConfigModule],
      inject: [ConfigService],
      useFactory: (config: ConfigService) => ({
        uri: config.get<string>('MONGO_URI'),
      }),
    }),
    MongooseModule.forFeature([{ name: User.name, schema: UserSchema }]),
  ],
  seeders: [UserSeeder],
});

Run it ​

Add a script to package.json:

json
{
  "scripts": {
    "seed": "nest-seed"
  }
}

Then run the seeders:

bash
npm run seed
bash
npm run seed -- --refresh
bash
npm run seed -- --name users

Refresh order

On --refresh, seeders are dropped in reverse order so foreign-key-style references unwind safely. List parents first in the seeders array.

Seeding relationships ​

To link documents across collections, pass the referenced id as an override. Override keys do not need to be @Factory fields on the factory — they are merged into every generated object.

ts
const factory = DataFactory.createForClass(PostFactory);
for (const user of users) {
  const posts = factory.generate(3, { author: user._id }); // author is an override, not a @Factory field
  await this.postModel.insertMany(posts);
}

Next steps ​

Released under the MIT License.