@ackplus/nest-seeder 1.1.8 → 1.1.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +22 -0
- package/QUICKSTART.md +213 -0
- package/README.md +1080 -6
- package/{src → dist}/cli.d.ts +0 -1
- package/{src → dist}/cli.js +42 -14
- package/dist/cli.js.map +1 -0
- package/{src → dist}/index.d.ts +0 -1
- package/dist/index.js +25 -0
- package/dist/index.js.map +1 -0
- package/{src → dist}/lib/decorators/factory.decorator.d.ts +0 -1
- package/{src → dist}/lib/decorators/factory.decorator.js +1 -0
- package/dist/lib/decorators/factory.decorator.js.map +1 -0
- package/{src → dist}/lib/factory/data.factory.d.ts +0 -1
- package/{src → dist}/lib/factory/data.factory.js +1 -11
- package/dist/lib/factory/data.factory.js.map +1 -0
- package/{src → dist}/lib/index.d.ts +0 -1
- package/dist/lib/index.js +21 -0
- package/dist/lib/index.js.map +1 -0
- package/{src → dist}/lib/interfaces/factory.interface.d.ts +0 -1
- package/{src → dist}/lib/interfaces/factory.interface.js +1 -0
- package/dist/lib/interfaces/factory.interface.js.map +1 -0
- package/{src → dist}/lib/interfaces/index.d.ts +0 -1
- package/dist/lib/interfaces/index.js +19 -0
- package/dist/lib/interfaces/index.js.map +1 -0
- package/{src → dist}/lib/interfaces/property-metadata.interface.d.ts +0 -1
- package/{src → dist}/lib/interfaces/property-metadata.interface.js +1 -0
- package/dist/lib/interfaces/property-metadata.interface.js.map +1 -0
- package/{src → dist}/lib/interfaces/seeder-module-async-options.interface.d.ts +0 -19
- package/{src → dist}/lib/interfaces/seeder-module-async-options.interface.js +1 -0
- package/dist/lib/interfaces/seeder-module-async-options.interface.js.map +1 -0
- package/dist/lib/interfaces/seeder-options-factory.interface.d.ts +4 -0
- package/{src → dist}/lib/interfaces/seeder-options-factory.interface.js +1 -0
- package/dist/lib/interfaces/seeder-options-factory.interface.js.map +1 -0
- package/{src → dist}/lib/seeder/seeder.d.ts +0 -1
- package/{src → dist}/lib/seeder/seeder.interface.d.ts +0 -1
- package/{src → dist}/lib/seeder/seeder.interface.js +1 -0
- package/dist/lib/seeder/seeder.interface.js.map +1 -0
- package/{src → dist}/lib/seeder/seeder.js +5 -2
- package/dist/lib/seeder/seeder.js.map +1 -0
- package/{src → dist}/lib/seeder/seeder.module.d.ts +0 -1
- package/{src → dist}/lib/seeder/seeder.module.js +9 -3
- package/dist/lib/seeder/seeder.module.js.map +1 -0
- package/{src → dist}/lib/seeder/seeder.service.d.ts +1 -2
- package/{src → dist}/lib/seeder/seeder.service.js +17 -6
- package/dist/lib/seeder/seeder.service.js.map +1 -0
- package/{src → dist}/lib/storages/factory.metadata.storage.d.ts +0 -1
- package/{src → dist}/lib/storages/factory.metadata.storage.js +2 -3
- package/dist/lib/storages/factory.metadata.storage.js.map +1 -0
- package/dist/tsconfig.build.tsbuildinfo +1 -0
- package/examples/post.seeder.example.ts +65 -0
- package/examples/product.factory.example.ts +84 -0
- package/examples/seed.script.example.ts +32 -0
- package/examples/seeder.config.example.ts +50 -0
- package/examples/user.factory.example.ts +59 -0
- package/examples/user.seeder.example.ts +55 -0
- package/package.json +107 -6
- package/src/cli.d.ts.map +0 -1
- package/src/index.d.ts.map +0 -1
- package/src/index.js +0 -11
- package/src/lib/decorators/factory.decorator.d.ts.map +0 -1
- package/src/lib/factory/data.factory.d.ts.map +0 -1
- package/src/lib/index.d.ts.map +0 -1
- package/src/lib/index.js +0 -7
- package/src/lib/interfaces/factory.interface.d.ts.map +0 -1
- package/src/lib/interfaces/index.d.ts.map +0 -1
- package/src/lib/interfaces/index.js +0 -5
- package/src/lib/interfaces/property-metadata.interface.d.ts.map +0 -1
- package/src/lib/interfaces/seeder-module-async-options.interface.d.ts.map +0 -1
- package/src/lib/interfaces/seeder-options-factory.interface.d.ts +0 -9
- package/src/lib/interfaces/seeder-options-factory.interface.d.ts.map +0 -1
- package/src/lib/seeder/seeder.d.ts.map +0 -1
- package/src/lib/seeder/seeder.interface.d.ts.map +0 -1
- package/src/lib/seeder/seeder.module.d.ts.map +0 -1
- package/src/lib/seeder/seeder.service.d.ts.map +0 -1
- package/src/lib/storages/factory.metadata.storage.d.ts.map +0 -1
package/README.md
CHANGED
|
@@ -1,11 +1,1085 @@
|
|
|
1
|
-
# nest-
|
|
1
|
+
# @ackplus/nest-seeder
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
<p align="center">
|
|
4
|
+
<a href="http://nestjs.com/" target="blank"><img src="https://nestjs.com/img/logo-small.svg" width="120" alt="Nest Logo" /></a>
|
|
5
|
+
</p>
|
|
4
6
|
|
|
5
|
-
|
|
7
|
+
A powerful and flexible database seeding library for NestJS applications with support for factories, data generation using Faker.js, and CLI commands.
|
|
6
8
|
|
|
7
|
-
|
|
9
|
+
## 📋 Table of Contents
|
|
8
10
|
|
|
9
|
-
|
|
11
|
+
- [Features](#-features)
|
|
12
|
+
- [Installation](#-installation)
|
|
13
|
+
- [Quick Start](#-quick-start)
|
|
14
|
+
- [Configuration](#-configuration)
|
|
15
|
+
- [Basic Setup](#basic-setup)
|
|
16
|
+
- [Async Configuration](#async-configuration)
|
|
17
|
+
- [Creating Seeders](#-creating-seeders)
|
|
18
|
+
- [Basic Seeder](#basic-seeder)
|
|
19
|
+
- [Using TypeORM](#using-typeorm)
|
|
20
|
+
- [Using Mongoose](#using-mongoose)
|
|
21
|
+
- [Using Prisma](#using-prisma)
|
|
22
|
+
- [Data Factories](#-data-factories)
|
|
23
|
+
- [Basic Factory](#basic-factory)
|
|
24
|
+
- [Factory with Dependencies](#factory-with-dependencies)
|
|
25
|
+
- [Custom Generators](#custom-generators)
|
|
26
|
+
- [CLI Usage](#-cli-usage)
|
|
27
|
+
- [Setup CLI](#setup-cli)
|
|
28
|
+
- [CLI Commands](#cli-commands)
|
|
29
|
+
- [Configuration File](#configuration-file)
|
|
30
|
+
- [Programmatic Usage](#-programmatic-usage)
|
|
31
|
+
- [Advanced Examples](#-advanced-examples)
|
|
32
|
+
- [API Reference](#-api-reference)
|
|
33
|
+
- [Publishing](#-publishing)
|
|
10
34
|
|
|
11
|
-
|
|
35
|
+
## ✨ Features
|
|
36
|
+
|
|
37
|
+
- 🎯 **Type-safe** - Full TypeScript support
|
|
38
|
+
- 🏭 **Factory Pattern** - Generate fake data easily with decorators
|
|
39
|
+
- 🎲 **Faker.js Integration** - Built-in support for realistic fake data
|
|
40
|
+
- 🔄 **Refresh Mode** - Drop existing data before seeding
|
|
41
|
+
- 🎯 **Selective Seeding** - Run specific seeders by name
|
|
42
|
+
- 📦 **Multiple ORMs** - Works with TypeORM, Mongoose, Prisma, and more
|
|
43
|
+
- 🖥️ **CLI Support** - Run seeders from command line
|
|
44
|
+
- ⚙️ **Flexible Configuration** - Sync and async configuration options
|
|
45
|
+
- 🔗 **Dependency Management** - Handle relationships between seeders
|
|
46
|
+
|
|
47
|
+
## 📦 Installation
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
# Using npm
|
|
51
|
+
npm install @ackplus/nest-seeder @faker-js/faker
|
|
52
|
+
|
|
53
|
+
# Using yarn
|
|
54
|
+
yarn add @ackplus/nest-seeder @faker-js/faker
|
|
55
|
+
|
|
56
|
+
# Using pnpm
|
|
57
|
+
pnpm add @ackplus/nest-seeder @faker-js/faker
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### Development Dependencies
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
# If using TypeScript files for seeders (recommended)
|
|
64
|
+
npm install -D ts-node typescript
|
|
65
|
+
|
|
66
|
+
# If using CLI
|
|
67
|
+
npm install -D @nestjs/cli
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## 🚀 Quick Start
|
|
71
|
+
|
|
72
|
+
### 1. Create a Factory Class
|
|
73
|
+
|
|
74
|
+
Create a factory class with the `@Factory` decorator to define how to generate fake data:
|
|
75
|
+
|
|
76
|
+
```typescript
|
|
77
|
+
// src/database/factories/user.factory.ts
|
|
78
|
+
import { Factory } from '@ackplus/nest-seeder';
|
|
79
|
+
|
|
80
|
+
export class UserFactory {
|
|
81
|
+
@Factory((faker) => faker.internet.email())
|
|
82
|
+
email: string;
|
|
83
|
+
|
|
84
|
+
@Factory((faker) => faker.person.firstName())
|
|
85
|
+
firstName: string;
|
|
86
|
+
|
|
87
|
+
@Factory((faker) => faker.person.lastName())
|
|
88
|
+
lastName: string;
|
|
89
|
+
|
|
90
|
+
@Factory((faker) => faker.internet.password())
|
|
91
|
+
password: string;
|
|
92
|
+
|
|
93
|
+
@Factory((faker) => faker.datatype.boolean())
|
|
94
|
+
isActive: boolean;
|
|
95
|
+
}
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### 2. Create a Seeder
|
|
99
|
+
|
|
100
|
+
Create a seeder class that implements the `Seeder` interface:
|
|
101
|
+
|
|
102
|
+
```typescript
|
|
103
|
+
// src/database/seeders/user.seeder.ts
|
|
104
|
+
import { Injectable } from '@nestjs/common';
|
|
105
|
+
import { InjectRepository } from '@nestjs/typeorm';
|
|
106
|
+
import { Repository } from 'typeorm';
|
|
107
|
+
import { Seeder, SeederServiceOptions, DataFactory } from '@ackplus/nest-seeder';
|
|
108
|
+
import { User } from '../entities/user.entity';
|
|
109
|
+
import { UserFactory } from '../factories/user.factory';
|
|
110
|
+
|
|
111
|
+
@Injectable()
|
|
112
|
+
export class UserSeeder implements Seeder {
|
|
113
|
+
constructor(
|
|
114
|
+
@InjectRepository(User)
|
|
115
|
+
private readonly userRepository: Repository<User>,
|
|
116
|
+
) {}
|
|
117
|
+
|
|
118
|
+
async seed(options: SeederServiceOptions): Promise<void> {
|
|
119
|
+
// Generate 10 fake users
|
|
120
|
+
const factory = DataFactory.createForClass(UserFactory);
|
|
121
|
+
const users = factory.generate(10);
|
|
122
|
+
|
|
123
|
+
// Save to database
|
|
124
|
+
await this.userRepository.save(users);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
async drop(options: SeederServiceOptions): Promise<void> {
|
|
128
|
+
// Clean up - delete all users
|
|
129
|
+
await this.userRepository.delete({});
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
### 3. Register Seeder Module
|
|
135
|
+
|
|
136
|
+
```typescript
|
|
137
|
+
// src/app.module.ts
|
|
138
|
+
import { Module } from '@nestjs/common';
|
|
139
|
+
import { TypeOrmModule } from '@nestjs/typeorm';
|
|
140
|
+
import { SeederModule } from '@ackplus/nest-seeder';
|
|
141
|
+
import { User } from './database/entities/user.entity';
|
|
142
|
+
import { UserSeeder } from './database/seeders/user.seeder';
|
|
143
|
+
|
|
144
|
+
@Module({
|
|
145
|
+
imports: [
|
|
146
|
+
TypeOrmModule.forRoot({
|
|
147
|
+
type: 'postgres',
|
|
148
|
+
host: 'localhost',
|
|
149
|
+
port: 5432,
|
|
150
|
+
username: 'postgres',
|
|
151
|
+
password: 'postgres',
|
|
152
|
+
database: 'mydb',
|
|
153
|
+
entities: [User],
|
|
154
|
+
synchronize: true,
|
|
155
|
+
}),
|
|
156
|
+
TypeOrmModule.forFeature([User]),
|
|
157
|
+
SeederModule.register({
|
|
158
|
+
seeders: [UserSeeder],
|
|
159
|
+
}),
|
|
160
|
+
],
|
|
161
|
+
})
|
|
162
|
+
export class AppModule {}
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
### 4. Run the Seeder
|
|
166
|
+
|
|
167
|
+
```typescript
|
|
168
|
+
// src/seed.ts
|
|
169
|
+
import { NestFactory } from '@nestjs/core';
|
|
170
|
+
import { SeederService } from '@ackplus/nest-seeder';
|
|
171
|
+
import { AppModule } from './app.module';
|
|
172
|
+
|
|
173
|
+
async function bootstrap() {
|
|
174
|
+
const app = await NestFactory.createApplicationContext(AppModule);
|
|
175
|
+
const seeder = app.get(SeederService);
|
|
176
|
+
|
|
177
|
+
await seeder.run();
|
|
178
|
+
|
|
179
|
+
await app.close();
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
bootstrap();
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
Run it:
|
|
186
|
+
```bash
|
|
187
|
+
ts-node src/seed.ts
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
## ⚙️ Configuration
|
|
191
|
+
|
|
192
|
+
### Basic Setup
|
|
193
|
+
|
|
194
|
+
Register the `SeederModule` with your seeders:
|
|
195
|
+
|
|
196
|
+
```typescript
|
|
197
|
+
import { SeederModule } from '@ackplus/nest-seeder';
|
|
198
|
+
|
|
199
|
+
@Module({
|
|
200
|
+
imports: [
|
|
201
|
+
SeederModule.register({
|
|
202
|
+
seeders: [UserSeeder, PostSeeder, CommentSeeder],
|
|
203
|
+
imports: [TypeOrmModule.forFeature([User, Post, Comment])],
|
|
204
|
+
providers: [/* additional providers */],
|
|
205
|
+
}),
|
|
206
|
+
],
|
|
207
|
+
})
|
|
208
|
+
export class AppModule {}
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
### Async Configuration
|
|
212
|
+
|
|
213
|
+
For dynamic configuration (e.g., loading from ConfigService):
|
|
214
|
+
|
|
215
|
+
#### Using Factory
|
|
216
|
+
|
|
217
|
+
```typescript
|
|
218
|
+
import { SeederModule } from '@ackplus/nest-seeder';
|
|
219
|
+
import { ConfigModule, ConfigService } from '@nestjs/config';
|
|
220
|
+
|
|
221
|
+
@Module({
|
|
222
|
+
imports: [
|
|
223
|
+
SeederModule.forRootAsync({
|
|
224
|
+
imports: [ConfigModule, TypeOrmModule.forFeature([User, Post])],
|
|
225
|
+
inject: [UserSeeder, PostSeeder],
|
|
226
|
+
useFactory: async (config: ConfigService) => ({
|
|
227
|
+
seeders: [UserSeeder, PostSeeder],
|
|
228
|
+
refresh: config.get('SEED_REFRESH', false),
|
|
229
|
+
dummyData: config.get('SEED_DUMMY_DATA', false),
|
|
230
|
+
}),
|
|
231
|
+
isGlobal: true,
|
|
232
|
+
}),
|
|
233
|
+
],
|
|
234
|
+
})
|
|
235
|
+
export class AppModule {}
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
#### Using Class
|
|
239
|
+
|
|
240
|
+
```typescript
|
|
241
|
+
import { Injectable } from '@nestjs/common';
|
|
242
|
+
import { SeederOptionsFactory, SeederModuleOptions } from '@ackplus/nest-seeder';
|
|
243
|
+
import { ConfigService } from '@nestjs/config';
|
|
244
|
+
|
|
245
|
+
@Injectable()
|
|
246
|
+
export class SeederConfigService implements SeederOptionsFactory {
|
|
247
|
+
constructor(private configService: ConfigService) {}
|
|
248
|
+
|
|
249
|
+
createSeederOptions(): SeederModuleOptions {
|
|
250
|
+
return {
|
|
251
|
+
seeders: [UserSeeder, PostSeeder],
|
|
252
|
+
refresh: this.configService.get('SEED_REFRESH', false),
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
@Module({
|
|
258
|
+
imports: [
|
|
259
|
+
SeederModule.forRootAsync({
|
|
260
|
+
imports: [ConfigModule],
|
|
261
|
+
useClass: SeederConfigService,
|
|
262
|
+
isGlobal: true,
|
|
263
|
+
}),
|
|
264
|
+
],
|
|
265
|
+
})
|
|
266
|
+
export class AppModule {}
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
## 🌱 Creating Seeders
|
|
270
|
+
|
|
271
|
+
### Basic Seeder
|
|
272
|
+
|
|
273
|
+
Every seeder must implement the `Seeder` interface with `seed()` and `drop()` methods:
|
|
274
|
+
|
|
275
|
+
```typescript
|
|
276
|
+
import { Injectable } from '@nestjs/common';
|
|
277
|
+
import { Seeder, SeederServiceOptions } from '@ackplus/nest-seeder';
|
|
278
|
+
|
|
279
|
+
@Injectable()
|
|
280
|
+
export class BasicSeeder implements Seeder {
|
|
281
|
+
async seed(options: SeederServiceOptions): Promise<void> {
|
|
282
|
+
console.log('Seeding data...');
|
|
283
|
+
// Your seeding logic here
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
async drop(options: SeederServiceOptions): Promise<void> {
|
|
287
|
+
console.log('Dropping data...');
|
|
288
|
+
// Your cleanup logic here
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
### Using TypeORM
|
|
294
|
+
|
|
295
|
+
```typescript
|
|
296
|
+
import { Injectable } from '@nestjs/common';
|
|
297
|
+
import { InjectRepository } from '@nestjs/typeorm';
|
|
298
|
+
import { Repository } from 'typeorm';
|
|
299
|
+
import { Seeder, SeederServiceOptions, DataFactory } from '@ackplus/nest-seeder';
|
|
300
|
+
import { User } from '../entities/user.entity';
|
|
301
|
+
import { UserFactory } from '../factories/user.factory';
|
|
302
|
+
|
|
303
|
+
@Injectable()
|
|
304
|
+
export class UserSeeder implements Seeder {
|
|
305
|
+
constructor(
|
|
306
|
+
@InjectRepository(User)
|
|
307
|
+
private readonly userRepository: Repository<User>,
|
|
308
|
+
) {}
|
|
309
|
+
|
|
310
|
+
async seed(options: SeederServiceOptions): Promise<void> {
|
|
311
|
+
const factory = DataFactory.createForClass(UserFactory);
|
|
312
|
+
|
|
313
|
+
// Generate different amounts based on options
|
|
314
|
+
const count = options.dummyData ? 100 : 10;
|
|
315
|
+
const users = factory.generate(count);
|
|
316
|
+
|
|
317
|
+
// Insert in batches for better performance
|
|
318
|
+
const batchSize = 50;
|
|
319
|
+
for (let i = 0; i < users.length; i += batchSize) {
|
|
320
|
+
const batch = users.slice(i, i + batchSize);
|
|
321
|
+
await this.userRepository.save(batch);
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
console.log(`✅ Seeded ${users.length} users`);
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
async drop(options: SeederServiceOptions): Promise<void> {
|
|
328
|
+
await this.userRepository.delete({});
|
|
329
|
+
console.log('✅ Dropped all users');
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
### Using Mongoose
|
|
335
|
+
|
|
336
|
+
```typescript
|
|
337
|
+
import { Injectable } from '@nestjs/common';
|
|
338
|
+
import { InjectModel } from '@nestjs/mongoose';
|
|
339
|
+
import { Model } from 'mongoose';
|
|
340
|
+
import { Seeder, SeederServiceOptions, DataFactory } from '@ackplus/nest-seeder';
|
|
341
|
+
import { User, UserDocument } from '../schemas/user.schema';
|
|
342
|
+
import { UserFactory } from '../factories/user.factory';
|
|
343
|
+
|
|
344
|
+
@Injectable()
|
|
345
|
+
export class UserSeeder implements Seeder {
|
|
346
|
+
constructor(
|
|
347
|
+
@InjectModel(User.name)
|
|
348
|
+
private userModel: Model<UserDocument>,
|
|
349
|
+
) {}
|
|
350
|
+
|
|
351
|
+
async seed(options: SeederServiceOptions): Promise<void> {
|
|
352
|
+
const factory = DataFactory.createForClass(UserFactory);
|
|
353
|
+
const users = factory.generate(10);
|
|
354
|
+
|
|
355
|
+
await this.userModel.insertMany(users);
|
|
356
|
+
console.log('✅ Seeded 10 users');
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
async drop(options: SeederServiceOptions): Promise<void> {
|
|
360
|
+
await this.userModel.deleteMany({});
|
|
361
|
+
console.log('✅ Dropped all users');
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
### Using Prisma
|
|
367
|
+
|
|
368
|
+
```typescript
|
|
369
|
+
import { Injectable } from '@nestjs/common';
|
|
370
|
+
import { Seeder, SeederServiceOptions, DataFactory } from '@ackplus/nest-seeder';
|
|
371
|
+
import { PrismaService } from '../prisma.service';
|
|
372
|
+
import { UserFactory } from '../factories/user.factory';
|
|
373
|
+
|
|
374
|
+
@Injectable()
|
|
375
|
+
export class UserSeeder implements Seeder {
|
|
376
|
+
constructor(private readonly prisma: PrismaService) {}
|
|
377
|
+
|
|
378
|
+
async seed(options: SeederServiceOptions): Promise<void> {
|
|
379
|
+
const factory = DataFactory.createForClass(UserFactory);
|
|
380
|
+
const users = factory.generate(10);
|
|
381
|
+
|
|
382
|
+
// Use createMany for better performance
|
|
383
|
+
await this.prisma.user.createMany({
|
|
384
|
+
data: users,
|
|
385
|
+
skipDuplicates: true,
|
|
386
|
+
});
|
|
387
|
+
|
|
388
|
+
console.log('✅ Seeded 10 users');
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
async drop(options: SeederServiceOptions): Promise<void> {
|
|
392
|
+
await this.prisma.user.deleteMany({});
|
|
393
|
+
console.log('✅ Dropped all users');
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
```
|
|
397
|
+
|
|
398
|
+
### Seeder with Relationships
|
|
399
|
+
|
|
400
|
+
```typescript
|
|
401
|
+
import { Injectable } from '@nestjs/common';
|
|
402
|
+
import { InjectRepository } from '@nestjs/typeorm';
|
|
403
|
+
import { Repository } from 'typeorm';
|
|
404
|
+
import { Seeder, SeederServiceOptions, DataFactory } from '@ackplus/nest-seeder';
|
|
405
|
+
import { Post } from '../entities/post.entity';
|
|
406
|
+
import { User } from '../entities/user.entity';
|
|
407
|
+
import { PostFactory } from '../factories/post.factory';
|
|
408
|
+
|
|
409
|
+
@Injectable()
|
|
410
|
+
export class PostSeeder implements Seeder {
|
|
411
|
+
constructor(
|
|
412
|
+
@InjectRepository(Post)
|
|
413
|
+
private readonly postRepository: Repository<Post>,
|
|
414
|
+
@InjectRepository(User)
|
|
415
|
+
private readonly userRepository: Repository<User>,
|
|
416
|
+
) {}
|
|
417
|
+
|
|
418
|
+
async seed(options: SeederServiceOptions): Promise<void> {
|
|
419
|
+
// Get existing users
|
|
420
|
+
const users = await this.userRepository.find();
|
|
421
|
+
|
|
422
|
+
if (users.length === 0) {
|
|
423
|
+
console.warn('⚠️ No users found. Please run UserSeeder first.');
|
|
424
|
+
return;
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
const factory = DataFactory.createForClass(PostFactory);
|
|
428
|
+
|
|
429
|
+
// Generate 5 posts per user
|
|
430
|
+
for (const user of users) {
|
|
431
|
+
const posts = factory.generate(5, {
|
|
432
|
+
authorId: user.id,
|
|
433
|
+
});
|
|
434
|
+
await this.postRepository.save(posts);
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
console.log(`✅ Seeded ${users.length * 5} posts`);
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
async drop(options: SeederServiceOptions): Promise<void> {
|
|
441
|
+
await this.postRepository.delete({});
|
|
442
|
+
console.log('✅ Dropped all posts');
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
```
|
|
446
|
+
|
|
447
|
+
## 🏭 Data Factories
|
|
448
|
+
|
|
449
|
+
### Basic Factory
|
|
450
|
+
|
|
451
|
+
Use the `@Factory` decorator to define how each property should be generated:
|
|
452
|
+
|
|
453
|
+
```typescript
|
|
454
|
+
import { Factory } from '@ackplus/nest-seeder';
|
|
455
|
+
|
|
456
|
+
export class UserFactory {
|
|
457
|
+
@Factory((faker) => faker.internet.email())
|
|
458
|
+
email: string;
|
|
459
|
+
|
|
460
|
+
@Factory((faker) => faker.person.firstName())
|
|
461
|
+
firstName: string;
|
|
462
|
+
|
|
463
|
+
@Factory((faker) => faker.person.lastName())
|
|
464
|
+
lastName: string;
|
|
465
|
+
|
|
466
|
+
@Factory((faker) => faker.internet.password({ length: 10 }))
|
|
467
|
+
password: string;
|
|
468
|
+
|
|
469
|
+
@Factory((faker) => faker.datatype.boolean())
|
|
470
|
+
isActive: boolean;
|
|
471
|
+
|
|
472
|
+
@Factory((faker) => faker.date.past())
|
|
473
|
+
createdAt: Date;
|
|
474
|
+
}
|
|
475
|
+
```
|
|
476
|
+
|
|
477
|
+
### Static Values
|
|
478
|
+
|
|
479
|
+
You can also provide static values instead of generators:
|
|
480
|
+
|
|
481
|
+
```typescript
|
|
482
|
+
export class AdminFactory {
|
|
483
|
+
@Factory('admin')
|
|
484
|
+
role: string;
|
|
485
|
+
|
|
486
|
+
@Factory(true)
|
|
487
|
+
isActive: boolean;
|
|
488
|
+
|
|
489
|
+
@Factory((faker) => faker.internet.email())
|
|
490
|
+
email: string;
|
|
491
|
+
}
|
|
492
|
+
```
|
|
493
|
+
|
|
494
|
+
### Factory with Dependencies
|
|
495
|
+
|
|
496
|
+
Use the second parameter to specify dependencies between properties:
|
|
497
|
+
|
|
498
|
+
```typescript
|
|
499
|
+
export class UserFactory {
|
|
500
|
+
@Factory((faker) => faker.person.firstName())
|
|
501
|
+
firstName: string;
|
|
502
|
+
|
|
503
|
+
@Factory((faker) => faker.person.lastName())
|
|
504
|
+
lastName: string;
|
|
505
|
+
|
|
506
|
+
// This field depends on firstName and lastName
|
|
507
|
+
@Factory((faker, ctx) => {
|
|
508
|
+
return `${ctx.firstName}.${ctx.lastName}@example.com`.toLowerCase();
|
|
509
|
+
}, ['firstName', 'lastName'])
|
|
510
|
+
email: string;
|
|
511
|
+
|
|
512
|
+
// This field depends on firstName and lastName
|
|
513
|
+
@Factory((faker, ctx) => {
|
|
514
|
+
return `${ctx.firstName} ${ctx.lastName}`;
|
|
515
|
+
}, ['firstName', 'lastName'])
|
|
516
|
+
fullName: string;
|
|
517
|
+
}
|
|
518
|
+
```
|
|
519
|
+
|
|
520
|
+
### Custom Generators
|
|
521
|
+
|
|
522
|
+
Create complex data with custom generator functions:
|
|
523
|
+
|
|
524
|
+
```typescript
|
|
525
|
+
export class ProductFactory {
|
|
526
|
+
@Factory((faker) => faker.commerce.productName())
|
|
527
|
+
name: string;
|
|
528
|
+
|
|
529
|
+
@Factory((faker) => faker.commerce.productDescription())
|
|
530
|
+
description: string;
|
|
531
|
+
|
|
532
|
+
@Factory((faker) => parseFloat(faker.commerce.price()))
|
|
533
|
+
price: number;
|
|
534
|
+
|
|
535
|
+
@Factory((faker) => faker.number.int({ min: 0, max: 1000 }))
|
|
536
|
+
stock: number;
|
|
537
|
+
|
|
538
|
+
@Factory((faker) => faker.helpers.arrayElement(['electronics', 'clothing', 'food', 'books']))
|
|
539
|
+
category: string;
|
|
540
|
+
|
|
541
|
+
@Factory((faker) => {
|
|
542
|
+
return {
|
|
543
|
+
weight: faker.number.float({ min: 0.1, max: 100, precision: 0.1 }),
|
|
544
|
+
dimensions: {
|
|
545
|
+
width: faker.number.int({ min: 1, max: 100 }),
|
|
546
|
+
height: faker.number.int({ min: 1, max: 100 }),
|
|
547
|
+
depth: faker.number.int({ min: 1, max: 100 }),
|
|
548
|
+
},
|
|
549
|
+
};
|
|
550
|
+
})
|
|
551
|
+
metadata: object;
|
|
552
|
+
|
|
553
|
+
@Factory((faker) => faker.helpers.arrayElements(['red', 'blue', 'green', 'yellow'], { min: 1, max: 3 }))
|
|
554
|
+
colors: string[];
|
|
555
|
+
}
|
|
556
|
+
```
|
|
557
|
+
|
|
558
|
+
### Using Factories
|
|
559
|
+
|
|
560
|
+
```typescript
|
|
561
|
+
import { DataFactory } from '@ackplus/nest-seeder';
|
|
562
|
+
import { UserFactory } from './user.factory';
|
|
563
|
+
|
|
564
|
+
// Create a factory
|
|
565
|
+
const factory = DataFactory.createForClass(UserFactory);
|
|
566
|
+
|
|
567
|
+
// Generate single object
|
|
568
|
+
const user = factory.generate(1)[0];
|
|
569
|
+
|
|
570
|
+
// Generate multiple objects
|
|
571
|
+
const users = factory.generate(10);
|
|
572
|
+
|
|
573
|
+
// Generate with custom values (override factory defaults)
|
|
574
|
+
const admins = factory.generate(5, {
|
|
575
|
+
role: 'admin',
|
|
576
|
+
isActive: true,
|
|
577
|
+
});
|
|
578
|
+
|
|
579
|
+
// Mix of factory-generated and custom values
|
|
580
|
+
const users = factory.generate(3, {
|
|
581
|
+
isActive: true, // Override this field
|
|
582
|
+
// Other fields will be generated by factory
|
|
583
|
+
});
|
|
584
|
+
```
|
|
585
|
+
|
|
586
|
+
## 🖥️ CLI Usage
|
|
587
|
+
|
|
588
|
+
### Setup CLI
|
|
589
|
+
|
|
590
|
+
#### 1. Install CLI dependencies
|
|
591
|
+
|
|
592
|
+
```bash
|
|
593
|
+
npm install -D ts-node typescript
|
|
594
|
+
```
|
|
595
|
+
|
|
596
|
+
#### 2. Add CLI script to package.json
|
|
597
|
+
|
|
598
|
+
```json
|
|
599
|
+
{
|
|
600
|
+
"scripts": {
|
|
601
|
+
"seed": "nest-seed -c ./src/database/seeder.config.ts",
|
|
602
|
+
"seed:refresh": "nest-seed -c ./src/database/seeder.config.ts --refresh",
|
|
603
|
+
"seed:user": "nest-seed -c ./src/database/seeder.config.ts --name UserSeeder"
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
```
|
|
607
|
+
|
|
608
|
+
#### 3. Create a configuration file
|
|
609
|
+
|
|
610
|
+
```typescript
|
|
611
|
+
// src/database/seeder.config.ts
|
|
612
|
+
import { TypeOrmModule } from '@nestjs/typeorm';
|
|
613
|
+
import { User } from './entities/user.entity';
|
|
614
|
+
import { Post } from './entities/post.entity';
|
|
615
|
+
import { UserSeeder } from './seeders/user.seeder';
|
|
616
|
+
import { PostSeeder } from './seeders/post.seeder';
|
|
617
|
+
|
|
618
|
+
export default {
|
|
619
|
+
imports: [
|
|
620
|
+
TypeOrmModule.forRoot({
|
|
621
|
+
type: 'postgres',
|
|
622
|
+
host: 'localhost',
|
|
623
|
+
port: 5432,
|
|
624
|
+
username: 'postgres',
|
|
625
|
+
password: 'postgres',
|
|
626
|
+
database: 'mydb',
|
|
627
|
+
entities: [User, Post],
|
|
628
|
+
synchronize: true,
|
|
629
|
+
}),
|
|
630
|
+
TypeOrmModule.forFeature([User, Post]),
|
|
631
|
+
],
|
|
632
|
+
seeders: [UserSeeder, PostSeeder],
|
|
633
|
+
};
|
|
634
|
+
```
|
|
635
|
+
|
|
636
|
+
### CLI Commands
|
|
637
|
+
|
|
638
|
+
#### Run all seeders
|
|
639
|
+
|
|
640
|
+
```bash
|
|
641
|
+
npm run seed
|
|
642
|
+
# or
|
|
643
|
+
nest-seed -c ./src/database/seeder.config.ts
|
|
644
|
+
```
|
|
645
|
+
|
|
646
|
+
#### Refresh mode (drop and reseed)
|
|
647
|
+
|
|
648
|
+
```bash
|
|
649
|
+
npm run seed:refresh
|
|
650
|
+
# or
|
|
651
|
+
nest-seed -c ./src/database/seeder.config.ts --refresh
|
|
652
|
+
# or short form
|
|
653
|
+
nest-seed -c ./src/database/seeder.config.ts -r
|
|
654
|
+
```
|
|
655
|
+
|
|
656
|
+
#### Run specific seeder(s)
|
|
657
|
+
|
|
658
|
+
```bash
|
|
659
|
+
# Single seeder
|
|
660
|
+
nest-seed -c ./src/database/seeder.config.ts --name UserSeeder
|
|
661
|
+
# or short form
|
|
662
|
+
nest-seed -c ./src/database/seeder.config.ts -n UserSeeder
|
|
663
|
+
|
|
664
|
+
# Multiple seeders
|
|
665
|
+
nest-seed -c ./src/database/seeder.config.ts -n UserSeeder -n PostSeeder
|
|
666
|
+
```
|
|
667
|
+
|
|
668
|
+
#### With dummy data flag
|
|
669
|
+
|
|
670
|
+
```bash
|
|
671
|
+
nest-seed -c ./src/database/seeder.config.ts --dummyData
|
|
672
|
+
# or short form
|
|
673
|
+
nest-seed -c ./src/database/seeder.config.ts -d
|
|
674
|
+
```
|
|
675
|
+
|
|
676
|
+
#### Combined options
|
|
677
|
+
|
|
678
|
+
```bash
|
|
679
|
+
# Refresh and run specific seeder with dummy data
|
|
680
|
+
nest-seed -c ./src/database/seeder.config.ts -r -n UserSeeder -d
|
|
681
|
+
```
|
|
682
|
+
|
|
683
|
+
### CLI Help
|
|
684
|
+
|
|
685
|
+
```bash
|
|
686
|
+
nest-seed --help
|
|
687
|
+
```
|
|
688
|
+
|
|
689
|
+
Output:
|
|
690
|
+
```
|
|
691
|
+
Options:
|
|
692
|
+
--help Show help [boolean]
|
|
693
|
+
--version Show version number [boolean]
|
|
694
|
+
--config, -c Path to seeder configuration file [required]
|
|
695
|
+
--refresh, -r Drop all data before seeding [boolean] [default: false]
|
|
696
|
+
--name, -n Specific seeder names to run [array]
|
|
697
|
+
--dummyData, -d Include dummy data [boolean] [default: false]
|
|
698
|
+
|
|
699
|
+
Examples:
|
|
700
|
+
nest-seed -c ./seeder.config.ts Run all seeders
|
|
701
|
+
nest-seed -c ./seeder.config.ts --refresh Drop and reseed all data
|
|
702
|
+
nest-seed -c ./seeder.config.ts --name UserSeeder Run specific seeder
|
|
703
|
+
```
|
|
704
|
+
|
|
705
|
+
### Configuration File
|
|
706
|
+
|
|
707
|
+
The configuration file should export a default object with the following structure:
|
|
708
|
+
|
|
709
|
+
```typescript
|
|
710
|
+
// seeder.config.ts
|
|
711
|
+
import { SeederModuleOptions } from '@ackplus/nest-seeder';
|
|
712
|
+
|
|
713
|
+
const config: SeederModuleOptions = {
|
|
714
|
+
// Required: Array of seeder providers
|
|
715
|
+
seeders: [UserSeeder, PostSeeder, CommentSeeder],
|
|
716
|
+
|
|
717
|
+
// Optional: Modules to import (e.g., TypeORM, Mongoose)
|
|
718
|
+
imports: [
|
|
719
|
+
TypeOrmModule.forRoot({ /* ... */ }),
|
|
720
|
+
TypeOrmModule.forFeature([User, Post, Comment]),
|
|
721
|
+
],
|
|
722
|
+
|
|
723
|
+
// Optional: Additional providers
|
|
724
|
+
providers: [PrismaService, CustomService],
|
|
725
|
+
};
|
|
726
|
+
|
|
727
|
+
export default config;
|
|
728
|
+
```
|
|
729
|
+
|
|
730
|
+
## 📝 Programmatic Usage
|
|
731
|
+
|
|
732
|
+
### Using Seeder Function
|
|
733
|
+
|
|
734
|
+
```typescript
|
|
735
|
+
// src/seed.ts
|
|
736
|
+
import { seeder } from '@ackplus/nest-seeder';
|
|
737
|
+
import { TypeOrmModule } from '@nestjs/typeorm';
|
|
738
|
+
import { User } from './entities/user.entity';
|
|
739
|
+
import { UserSeeder } from './seeders/user.seeder';
|
|
740
|
+
|
|
741
|
+
seeder({
|
|
742
|
+
imports: [
|
|
743
|
+
TypeOrmModule.forRoot({
|
|
744
|
+
type: 'postgres',
|
|
745
|
+
host: 'localhost',
|
|
746
|
+
port: 5432,
|
|
747
|
+
username: 'postgres',
|
|
748
|
+
password: 'postgres',
|
|
749
|
+
database: 'mydb',
|
|
750
|
+
entities: [User],
|
|
751
|
+
synchronize: true,
|
|
752
|
+
}),
|
|
753
|
+
TypeOrmModule.forFeature([User]),
|
|
754
|
+
],
|
|
755
|
+
}).run({
|
|
756
|
+
seeders: [UserSeeder],
|
|
757
|
+
});
|
|
758
|
+
```
|
|
759
|
+
|
|
760
|
+
Run with CLI arguments:
|
|
761
|
+
```bash
|
|
762
|
+
ts-node src/seed.ts --refresh
|
|
763
|
+
ts-node src/seed.ts --name UserSeeder
|
|
764
|
+
ts-node src/seed.ts -r -n UserSeeder -d
|
|
765
|
+
```
|
|
766
|
+
|
|
767
|
+
### Using SeederService Directly
|
|
768
|
+
|
|
769
|
+
```typescript
|
|
770
|
+
import { NestFactory } from '@nestjs/core';
|
|
771
|
+
import { SeederService } from '@ackplus/nest-seeder';
|
|
772
|
+
import { AppModule } from './app.module';
|
|
773
|
+
|
|
774
|
+
async function seed() {
|
|
775
|
+
const app = await NestFactory.createApplicationContext(AppModule);
|
|
776
|
+
const seeder = app.get(SeederService);
|
|
777
|
+
|
|
778
|
+
// Run all seeders
|
|
779
|
+
await seeder.run();
|
|
780
|
+
|
|
781
|
+
// Or just seed (without drop)
|
|
782
|
+
await seeder.seed();
|
|
783
|
+
|
|
784
|
+
// Or just drop
|
|
785
|
+
await seeder.drop();
|
|
786
|
+
|
|
787
|
+
await app.close();
|
|
788
|
+
}
|
|
789
|
+
|
|
790
|
+
seed();
|
|
791
|
+
```
|
|
792
|
+
|
|
793
|
+
## 🔥 Advanced Examples
|
|
794
|
+
|
|
795
|
+
### Conditional Seeding
|
|
796
|
+
|
|
797
|
+
```typescript
|
|
798
|
+
@Injectable()
|
|
799
|
+
export class UserSeeder implements Seeder {
|
|
800
|
+
constructor(
|
|
801
|
+
@InjectRepository(User)
|
|
802
|
+
private readonly userRepository: Repository<User>,
|
|
803
|
+
) {}
|
|
804
|
+
|
|
805
|
+
async seed(options: SeederServiceOptions): Promise<void> {
|
|
806
|
+
// Check if data already exists
|
|
807
|
+
const count = await this.userRepository.count();
|
|
808
|
+
if (count > 0 && !options.refresh) {
|
|
809
|
+
console.log('⏭️ Users already exist, skipping...');
|
|
810
|
+
return;
|
|
811
|
+
}
|
|
812
|
+
|
|
813
|
+
const factory = DataFactory.createForClass(UserFactory);
|
|
814
|
+
|
|
815
|
+
// Different amounts based on environment
|
|
816
|
+
const count = process.env.NODE_ENV === 'production' ? 10 : 100;
|
|
817
|
+
const users = factory.generate(count);
|
|
818
|
+
|
|
819
|
+
await this.userRepository.save(users);
|
|
820
|
+
console.log(`✅ Seeded ${users.length} users`);
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
async drop(options: SeederServiceOptions): Promise<void> {
|
|
824
|
+
await this.userRepository.delete({});
|
|
825
|
+
}
|
|
826
|
+
}
|
|
827
|
+
```
|
|
828
|
+
|
|
829
|
+
### Seeding with External Data
|
|
830
|
+
|
|
831
|
+
```typescript
|
|
832
|
+
@Injectable()
|
|
833
|
+
export class CountrySeeder implements Seeder {
|
|
834
|
+
async seed(options: SeederServiceOptions): Promise<void> {
|
|
835
|
+
const countries = [
|
|
836
|
+
{ code: 'US', name: 'United States' },
|
|
837
|
+
{ code: 'GB', name: 'United Kingdom' },
|
|
838
|
+
{ code: 'CA', name: 'Canada' },
|
|
839
|
+
// ... more countries
|
|
840
|
+
];
|
|
841
|
+
|
|
842
|
+
await this.countryRepository.save(countries);
|
|
843
|
+
console.log(`✅ Seeded ${countries.length} countries`);
|
|
844
|
+
}
|
|
845
|
+
|
|
846
|
+
async drop(options: SeederServiceOptions): Promise<void> {
|
|
847
|
+
await this.countryRepository.delete({});
|
|
848
|
+
}
|
|
849
|
+
}
|
|
850
|
+
```
|
|
851
|
+
|
|
852
|
+
### Seeding from JSON File
|
|
853
|
+
|
|
854
|
+
```typescript
|
|
855
|
+
import * as fs from 'fs';
|
|
856
|
+
import * as path from 'path';
|
|
857
|
+
|
|
858
|
+
@Injectable()
|
|
859
|
+
export class ProductSeeder implements Seeder {
|
|
860
|
+
async seed(options: SeederServiceOptions): Promise<void> {
|
|
861
|
+
const filePath = path.join(__dirname, '../data/products.json');
|
|
862
|
+
const data = JSON.parse(fs.readFileSync(filePath, 'utf8'));
|
|
863
|
+
|
|
864
|
+
await this.productRepository.save(data);
|
|
865
|
+
console.log(`✅ Seeded ${data.length} products from file`);
|
|
866
|
+
}
|
|
867
|
+
|
|
868
|
+
async drop(options: SeederServiceOptions): Promise<void> {
|
|
869
|
+
await this.productRepository.delete({});
|
|
870
|
+
}
|
|
871
|
+
}
|
|
872
|
+
```
|
|
873
|
+
|
|
874
|
+
### Transaction Support
|
|
875
|
+
|
|
876
|
+
```typescript
|
|
877
|
+
@Injectable()
|
|
878
|
+
export class UserSeeder implements Seeder {
|
|
879
|
+
constructor(
|
|
880
|
+
@InjectRepository(User)
|
|
881
|
+
private readonly userRepository: Repository<User>,
|
|
882
|
+
private readonly dataSource: DataSource,
|
|
883
|
+
) {}
|
|
884
|
+
|
|
885
|
+
async seed(options: SeederServiceOptions): Promise<void> {
|
|
886
|
+
const queryRunner = this.dataSource.createQueryRunner();
|
|
887
|
+
await queryRunner.connect();
|
|
888
|
+
await queryRunner.startTransaction();
|
|
889
|
+
|
|
890
|
+
try {
|
|
891
|
+
const factory = DataFactory.createForClass(UserFactory);
|
|
892
|
+
const users = factory.generate(10);
|
|
893
|
+
|
|
894
|
+
await queryRunner.manager.save(users);
|
|
895
|
+
await queryRunner.commitTransaction();
|
|
896
|
+
|
|
897
|
+
console.log('✅ Seeded 10 users');
|
|
898
|
+
} catch (error) {
|
|
899
|
+
await queryRunner.rollbackTransaction();
|
|
900
|
+
throw error;
|
|
901
|
+
} finally {
|
|
902
|
+
await queryRunner.release();
|
|
903
|
+
}
|
|
904
|
+
}
|
|
905
|
+
|
|
906
|
+
async drop(options: SeederServiceOptions): Promise<void> {
|
|
907
|
+
await this.userRepository.delete({});
|
|
908
|
+
}
|
|
909
|
+
}
|
|
910
|
+
```
|
|
911
|
+
|
|
912
|
+
### Multi-tenant Seeding
|
|
913
|
+
|
|
914
|
+
```typescript
|
|
915
|
+
@Injectable()
|
|
916
|
+
export class TenantSeeder implements Seeder {
|
|
917
|
+
constructor(
|
|
918
|
+
@InjectRepository(Tenant)
|
|
919
|
+
private readonly tenantRepository: Repository<Tenant>,
|
|
920
|
+
@InjectRepository(User)
|
|
921
|
+
private readonly userRepository: Repository<User>,
|
|
922
|
+
) {}
|
|
923
|
+
|
|
924
|
+
async seed(options: SeederServiceOptions): Promise<void> {
|
|
925
|
+
const tenantFactory = DataFactory.createForClass(TenantFactory);
|
|
926
|
+
const tenants = tenantFactory.generate(5);
|
|
927
|
+
const savedTenants = await this.tenantRepository.save(tenants);
|
|
928
|
+
|
|
929
|
+
const userFactory = DataFactory.createForClass(UserFactory);
|
|
930
|
+
|
|
931
|
+
// Create users for each tenant
|
|
932
|
+
for (const tenant of savedTenants) {
|
|
933
|
+
const users = userFactory.generate(10, {
|
|
934
|
+
tenantId: tenant.id,
|
|
935
|
+
});
|
|
936
|
+
await this.userRepository.save(users);
|
|
937
|
+
}
|
|
938
|
+
|
|
939
|
+
console.log('✅ Seeded 5 tenants with 10 users each');
|
|
940
|
+
}
|
|
941
|
+
|
|
942
|
+
async drop(options: SeederServiceOptions): Promise<void> {
|
|
943
|
+
await this.userRepository.delete({});
|
|
944
|
+
await this.tenantRepository.delete({});
|
|
945
|
+
}
|
|
946
|
+
}
|
|
947
|
+
```
|
|
948
|
+
|
|
949
|
+
## 📚 API Reference
|
|
950
|
+
|
|
951
|
+
### SeederModule
|
|
952
|
+
|
|
953
|
+
#### `SeederModule.register(options: SeederModuleOptions)`
|
|
954
|
+
|
|
955
|
+
Synchronously register the seeder module.
|
|
956
|
+
|
|
957
|
+
**Options:**
|
|
958
|
+
- `seeders`: Array of seeder providers
|
|
959
|
+
- `imports`: Modules to import
|
|
960
|
+
- `providers`: Additional providers
|
|
961
|
+
|
|
962
|
+
#### `SeederModule.forRootAsync(options: SeederModuleAsyncOptions)`
|
|
963
|
+
|
|
964
|
+
Asynchronously register the seeder module.
|
|
965
|
+
|
|
966
|
+
**Options:**
|
|
967
|
+
- `useFactory`: Factory function for async configuration
|
|
968
|
+
- `useClass`: Configuration class implementing `SeederOptionsFactory`
|
|
969
|
+
- `useExisting`: Existing provider for configuration
|
|
970
|
+
- `imports`: Modules to import
|
|
971
|
+
- `inject`: Dependencies to inject
|
|
972
|
+
- `isGlobal`: Make module global
|
|
973
|
+
|
|
974
|
+
### Seeder Interface
|
|
975
|
+
|
|
976
|
+
```typescript
|
|
977
|
+
interface Seeder {
|
|
978
|
+
seed(options: SeederServiceOptions): Promise<any>;
|
|
979
|
+
drop(options: SeederServiceOptions): Promise<any>;
|
|
980
|
+
}
|
|
981
|
+
```
|
|
982
|
+
|
|
983
|
+
### SeederServiceOptions
|
|
984
|
+
|
|
985
|
+
```typescript
|
|
986
|
+
interface SeederServiceOptions {
|
|
987
|
+
name?: string | string[]; // Specific seeders to run
|
|
988
|
+
refresh?: boolean; // Drop before seeding
|
|
989
|
+
dummyData?: boolean; // Flag for conditional logic
|
|
990
|
+
}
|
|
991
|
+
```
|
|
992
|
+
|
|
993
|
+
### DataFactory
|
|
994
|
+
|
|
995
|
+
#### `DataFactory.createForClass<T>(target: Type<T>): Factory`
|
|
996
|
+
|
|
997
|
+
Create a factory for a class with `@Factory` decorators.
|
|
998
|
+
|
|
999
|
+
**Returns:** Factory object with `generate()` method
|
|
1000
|
+
|
|
1001
|
+
```typescript
|
|
1002
|
+
interface Factory {
|
|
1003
|
+
generate(count: number, values?: Record<string, any>): Array<Record<string, any>>;
|
|
1004
|
+
}
|
|
1005
|
+
```
|
|
1006
|
+
|
|
1007
|
+
### @Factory Decorator
|
|
1008
|
+
|
|
1009
|
+
```typescript
|
|
1010
|
+
@Factory(
|
|
1011
|
+
generator: FactoryValueGenerator | FactoryValue,
|
|
1012
|
+
dependsOn?: string[]
|
|
1013
|
+
)
|
|
1014
|
+
```
|
|
1015
|
+
|
|
1016
|
+
**Parameters:**
|
|
1017
|
+
- `generator`: Function `(faker, ctx) => value` or static value
|
|
1018
|
+
- `dependsOn`: Array of property names this field depends on
|
|
1019
|
+
|
|
1020
|
+
### SeederService
|
|
1021
|
+
|
|
1022
|
+
#### Methods
|
|
1023
|
+
|
|
1024
|
+
- `run()`: Execute seed or drop+seed based on options
|
|
1025
|
+
- `seed()`: Run seed method on all seeders
|
|
1026
|
+
- `drop()`: Run drop method on all seeders
|
|
1027
|
+
- `getSeederToRun()`: Get list of seeders to run based on options
|
|
1028
|
+
|
|
1029
|
+
## 📦 Publishing
|
|
1030
|
+
|
|
1031
|
+
This package uses automated GitHub Actions for publishing.
|
|
1032
|
+
|
|
1033
|
+
### Using CLI Script
|
|
1034
|
+
|
|
1035
|
+
```bash
|
|
1036
|
+
# Patch version (0.0.1 -> 0.0.2)
|
|
1037
|
+
npm run publish:patch
|
|
1038
|
+
|
|
1039
|
+
# Minor version (0.0.1 -> 0.1.0)
|
|
1040
|
+
npm run publish:minor
|
|
1041
|
+
|
|
1042
|
+
# Major version (0.0.1 -> 1.0.0)
|
|
1043
|
+
npm run publish:major
|
|
1044
|
+
```
|
|
1045
|
+
|
|
1046
|
+
The script will:
|
|
1047
|
+
1. Update package version
|
|
1048
|
+
2. Create git tag
|
|
1049
|
+
3. Ask to push to remote (triggers GitHub Action)
|
|
1050
|
+
4. Commit version change
|
|
1051
|
+
|
|
1052
|
+
### Manual Publishing
|
|
1053
|
+
|
|
1054
|
+
```bash
|
|
1055
|
+
# Create and push a tag
|
|
1056
|
+
git tag v1.0.0
|
|
1057
|
+
git push origin v1.0.0
|
|
1058
|
+
|
|
1059
|
+
# GitHub Action will automatically publish to npm
|
|
1060
|
+
```
|
|
1061
|
+
|
|
1062
|
+
## 🤝 Contributing
|
|
1063
|
+
|
|
1064
|
+
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
1065
|
+
|
|
1066
|
+
## 📄 License
|
|
1067
|
+
|
|
1068
|
+
This project is licensed under the MIT License.
|
|
1069
|
+
|
|
1070
|
+
## 🙏 Acknowledgments
|
|
1071
|
+
|
|
1072
|
+
- Built with [NestJS](https://nestjs.com/)
|
|
1073
|
+
- Powered by [Faker.js](https://fakerjs.dev/)
|
|
1074
|
+
- Inspired by database seeding patterns from Laravel and other frameworks
|
|
1075
|
+
|
|
1076
|
+
## 📮 Support
|
|
1077
|
+
|
|
1078
|
+
If you have any questions or need help, please:
|
|
1079
|
+
- Open an issue on GitHub
|
|
1080
|
+
- Check existing documentation
|
|
1081
|
+
- Review examples in the repository
|
|
1082
|
+
|
|
1083
|
+
---
|
|
1084
|
+
|
|
1085
|
+
Made with ❤️ for the NestJS community
|