TypeORM
This guide walks you through seeding a TypeORM database end-to-end with @ackplus/nest-seeder: defining an entity, writing a factory, building a seeder with a correct drop(), and wiring up the config for Postgres, MySQL, and SQLite. It also covers relationships and batch inserts.
Install
Install the library, Faker, and the TypeORM packages, plus the driver for your database.
npm install @ackplus/nest-seeder @faker-js/faker @nestjs/typeorm typeorm pgnpm install @ackplus/nest-seeder @faker-js/faker @nestjs/typeorm typeorm mysql2npm install @ackplus/nest-seeder @faker-js/faker @nestjs/typeorm typeorm better-sqlite3TIP
Using a .ts config file? Also install the dev dependencies: npm install -D ts-node typescript.
1. Define your entity
Nothing special here — a standard TypeORM entity.
// src/database/entities/user.entity.ts
import { Entity, PrimaryGeneratedColumn, Column } from 'typeorm';
@Entity('users')
export class User {
@PrimaryGeneratedColumn()
id: number;
@Column()
firstName: string;
@Column()
lastName: string;
@Column({ unique: true })
email: string;
@Column({ default: 'user' })
role: string;
}2. Write a factory
A factory describes how to generate one fake row. Each @Factory property receives a Faker instance, and fields can derive from earlier ones using dependsOn.
// 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;
}See the Factories guide for the full @Factory API.
3. Write the seeder
A seeder implements the Seeder interface. Inject your repository, generate rows with DataFactory, and persist them with save().
// src/database/seeders/user.seeder.ts
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { Seeder, SeederName, DataFactory } from '@ackplus/nest-seeder';
import { User } from '../entities/user.entity';
import { UserFactory } from '../factories/user.factory';
@Injectable()
@SeederName('users')
export class UserSeeder implements Seeder {
constructor(
@InjectRepository(User) private readonly userRepository: Repository<User>,
) {}
async seed(): Promise<void> {
const factory = DataFactory.createForClass(UserFactory);
const users = factory.generate(10);
await this.userRepository.save(users);
}
async drop(): Promise<void> {
await this.userRepository.createQueryBuilder().delete().execute();
}
}TIP
@SeederName('users') gives the seeder a stable, minification-safe name. You can then target it from the CLI with nest-seed --name users.
Why the query builder in drop()?
drop() is called when you run with --refresh. To delete every row in a table you must use the query builder:
await this.userRepository.createQueryBuilder().delete().execute();WARNING
Do not use repo.delete({}) to clear a table. Modern TypeORM rejects empty criteria and throws:
Empty criteria(s) are not allowed for the delete method.Always use createQueryBuilder().delete().execute() instead.
4. Configure seeder.config.ts
Create seeder.config.ts at your project root. Register your TypeOrmModule connection with forRoot, expose your entities to the seeder context with forFeature, and list your seeders.
import { TypeOrmModule } from '@nestjs/typeorm';
import { defineSeederConfig } from '@ackplus/nest-seeder';
import { User } from './src/database/entities/user.entity';
import { UserSeeder } from './src/database/seeders/user.seeder';
export default defineSeederConfig({
imports: [
TypeOrmModule.forRoot({
type: 'postgres',
host: process.env.DB_HOST ?? 'localhost',
port: Number(process.env.DB_PORT ?? 5432),
username: process.env.DB_USERNAME ?? 'postgres',
password: process.env.DB_PASSWORD ?? 'postgres',
database: process.env.DB_DATABASE ?? 'app',
entities: [User],
synchronize: true,
}),
TypeOrmModule.forFeature([User]),
],
seeders: [UserSeeder],
});import { TypeOrmModule } from '@nestjs/typeorm';
import { defineSeederConfig } from '@ackplus/nest-seeder';
import { User } from './src/database/entities/user.entity';
import { UserSeeder } from './src/database/seeders/user.seeder';
export default defineSeederConfig({
imports: [
TypeOrmModule.forRoot({
type: 'mysql',
host: process.env.DB_HOST ?? 'localhost',
port: Number(process.env.DB_PORT ?? 3306),
username: process.env.DB_USERNAME ?? 'root',
password: process.env.DB_PASSWORD ?? 'root',
database: process.env.DB_DATABASE ?? 'app',
entities: [User],
synchronize: true,
}),
TypeOrmModule.forFeature([User]),
],
seeders: [UserSeeder],
});import { TypeOrmModule } from '@nestjs/typeorm';
import { defineSeederConfig } from '@ackplus/nest-seeder';
import { User } from './src/database/entities/user.entity';
import { UserSeeder } from './src/database/seeders/user.seeder';
export default defineSeederConfig({
imports: [
TypeOrmModule.forRoot({
type: 'better-sqlite3',
database: process.env.DB_DATABASE ?? 'app.sqlite',
entities: [User],
synchronize: true,
}),
TypeOrmModule.forFeature([User]),
],
seeders: [UserSeeder],
});INFO
defineSeederConfig is an identity helper that gives you full type-safety and autocompletion on the config object. It's optional but recommended.
WARNING
synchronize: true is convenient for local development and seeding throwaway databases, but never enable it against production data — it can drop columns and tables to match your entities.
Async config with ConfigService
If your connection details come from @nestjs/config, use forRootAsync instead. Add ConfigModule to the connection's imports and inject ConfigService.
import { ConfigModule, ConfigService } from '@nestjs/config';
import { TypeOrmModule } from '@nestjs/typeorm';
import { defineSeederConfig } from '@ackplus/nest-seeder';
import { User } from './src/database/entities/user.entity';
import { UserSeeder } from './src/database/seeders/user.seeder';
export default defineSeederConfig({
imports: [
TypeOrmModule.forRootAsync({
imports: [ConfigModule],
inject: [ConfigService],
useFactory: (config: ConfigService) => ({
type: 'postgres',
host: config.get('DB_HOST'),
port: Number(config.get('DB_PORT')),
username: config.get('DB_USERNAME'),
password: config.get('DB_PASSWORD'),
database: config.get('DB_DATABASE'),
entities: [User],
synchronize: true,
}),
}),
TypeOrmModule.forFeature([User]),
],
seeders: [UserSeeder],
});5. Run it
Add a script to package.json:
{
"scripts": {
"seed": "nest-seed"
}
}Then:
npm run seed # run all seeders
npm run seed -- --refresh # drop, then reseed
npm run seed -- --name users # run only the UserSeederSee the CLI reference for every flag.
Relationships
To seed related records, run the parent seeder first, then pass the foreign key as an override when generating children. Overrides may include keys that are not @Factory fields on the factory.
// src/database/factories/post.factory.ts
import { Factory } from '@ackplus/nest-seeder';
export class PostFactory {
@Factory((faker) => faker.lorem.sentence())
title: string;
@Factory((faker) => faker.lorem.paragraphs(2))
body: string;
}// inside PostSeeder.seed()
const factory = DataFactory.createForClass(PostFactory);
for (const user of users) {
const posts = factory.generate(3, { authorId: user.id }); // authorId is an override, not a @Factory field
await this.postRepository.save(posts);
}Order matters
List parents before children in your seeders array. Seeders run top-to-bottom, and on --refresh they are dropped in reverse order — which keeps foreign-key constraints happy. Put UserSeeder before PostSeeder:
seeders: [UserSeeder, PostSeeder],Batch inserts with save()
factory.generate(count) returns a plain array, and TypeORM's save() accepts an array — so a single call inserts the whole batch:
const factory = DataFactory.createForClass(UserFactory);
const users = factory.generate(1000);
await this.userRepository.save(users);For very large volumes you can chunk the work to avoid building one giant query:
const factory = DataFactory.createForClass(UserFactory);
for (let i = 0; i < 10; i++) {
const batch = factory.generate(1000);
await this.userRepository.save(batch);
}Next steps
- Recipes — common seeding patterns and snippets.
- Testing — using seeders to set up test data.
- CLI reference — all
nest-seedcommands and flags.