@ackplus/nest-seeder 1.1.12 โ 1.1.17
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/QUICKSTART.md +105 -108
- package/README.md +333 -802
- package/dist/cli.js +68 -13
- package/dist/cli.js.map +1 -1
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +14 -5
package/README.md
CHANGED
|
@@ -1,110 +1,84 @@
|
|
|
1
1
|
# @ackplus/nest-seeder
|
|
2
2
|
|
|
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>
|
|
6
|
-
|
|
7
3
|
A powerful and flexible database seeding library for NestJS applications with support for factories, data generation using Faker.js, and CLI commands.
|
|
8
4
|
|
|
9
|
-
## ๐ Table of Contents
|
|
10
|
-
|
|
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)
|
|
34
|
-
|
|
35
5
|
## โจ Features
|
|
36
6
|
|
|
37
|
-
-
|
|
38
|
-
- ๐ญ **Factory Pattern** - Generate
|
|
39
|
-
-
|
|
40
|
-
-
|
|
7
|
+
- ๐ฑ **CLI-Based** - Simple command-line interface, no app code modifications needed
|
|
8
|
+
- ๐ญ **Factory Pattern** - Generate realistic test data with Faker.js
|
|
9
|
+
- ๐ **Multiple ORMs** - Support for TypeORM, Mongoose, and Prisma
|
|
10
|
+
- ๐ฆ **Batch Operations** - Efficient bulk data insertion
|
|
41
11
|
- ๐ฏ **Selective Seeding** - Run specific seeders by name
|
|
42
|
-
-
|
|
43
|
-
-
|
|
44
|
-
-
|
|
45
|
-
- ๐ **Dependency Management** - Handle relationships between seeders
|
|
12
|
+
- ๐ฅ **Refresh Mode** - Drop existing data before seeding
|
|
13
|
+
- ๐งช **Test-Friendly** - Perfect for testing and development
|
|
14
|
+
- ๐ **TypeScript** - Full TypeScript support with type safety
|
|
46
15
|
|
|
47
16
|
## ๐ฆ Installation
|
|
48
17
|
|
|
49
18
|
```bash
|
|
50
|
-
# Using npm
|
|
51
19
|
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
|
|
20
|
+
# or
|
|
57
21
|
pnpm add @ackplus/nest-seeder @faker-js/faker
|
|
22
|
+
# or
|
|
23
|
+
yarn add @ackplus/nest-seeder @faker-js/faker
|
|
58
24
|
```
|
|
59
25
|
|
|
60
|
-
|
|
26
|
+
**For TypeScript config files**, also install:
|
|
61
27
|
|
|
62
28
|
```bash
|
|
63
|
-
# If using TypeScript files for seeders (recommended)
|
|
64
29
|
npm install -D ts-node typescript
|
|
65
|
-
|
|
66
|
-
# If using CLI
|
|
67
|
-
npm install -D @nestjs/cli
|
|
68
30
|
```
|
|
69
31
|
|
|
70
|
-
## ๐ Quick Start
|
|
32
|
+
## ๐ Quick Start (5 Steps)
|
|
33
|
+
|
|
34
|
+
### Step 1: Create Entity
|
|
35
|
+
|
|
36
|
+
```typescript
|
|
37
|
+
// src/entities/user.entity.ts
|
|
38
|
+
import { Entity, PrimaryGeneratedColumn, Column } from 'typeorm';
|
|
39
|
+
|
|
40
|
+
@Entity('users')
|
|
41
|
+
export class User {
|
|
42
|
+
@PrimaryGeneratedColumn()
|
|
43
|
+
id: number;
|
|
71
44
|
|
|
72
|
-
|
|
45
|
+
@Column()
|
|
46
|
+
name: string;
|
|
73
47
|
|
|
74
|
-
|
|
48
|
+
@Column({ unique: true })
|
|
49
|
+
email: string;
|
|
50
|
+
|
|
51
|
+
@Column()
|
|
52
|
+
role: string;
|
|
53
|
+
}
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### Step 2: Create Factory
|
|
75
57
|
|
|
76
58
|
```typescript
|
|
77
|
-
// src/
|
|
59
|
+
// src/factories/user.factory.ts
|
|
78
60
|
import { Factory } from '@ackplus/nest-seeder';
|
|
79
61
|
|
|
80
62
|
export class UserFactory {
|
|
63
|
+
@Factory((faker) => faker.person.fullName())
|
|
64
|
+
name: string;
|
|
65
|
+
|
|
81
66
|
@Factory((faker) => faker.internet.email())
|
|
82
67
|
email: string;
|
|
83
68
|
|
|
84
|
-
@Factory((faker) => faker.
|
|
85
|
-
|
|
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;
|
|
69
|
+
@Factory((faker) => faker.helpers.arrayElement(['admin', 'user', 'guest']))
|
|
70
|
+
role: string;
|
|
95
71
|
}
|
|
96
72
|
```
|
|
97
73
|
|
|
98
|
-
###
|
|
99
|
-
|
|
100
|
-
Create a seeder class that implements the `Seeder` interface:
|
|
74
|
+
### Step 3: Create Seeder
|
|
101
75
|
|
|
102
76
|
```typescript
|
|
103
|
-
// src/
|
|
77
|
+
// src/seeders/user.seeder.ts
|
|
104
78
|
import { Injectable } from '@nestjs/common';
|
|
105
79
|
import { InjectRepository } from '@nestjs/typeorm';
|
|
106
80
|
import { Repository } from 'typeorm';
|
|
107
|
-
import { Seeder,
|
|
81
|
+
import { Seeder, DataFactory } from '@ackplus/nest-seeder';
|
|
108
82
|
import { User } from '../entities/user.entity';
|
|
109
83
|
import { UserFactory } from '../factories/user.factory';
|
|
110
84
|
|
|
@@ -115,34 +89,41 @@ export class UserSeeder implements Seeder {
|
|
|
115
89
|
private readonly userRepository: Repository<User>,
|
|
116
90
|
) {}
|
|
117
91
|
|
|
118
|
-
async seed(
|
|
119
|
-
//
|
|
92
|
+
async seed(): Promise<void> {
|
|
93
|
+
// Create factory instance
|
|
120
94
|
const factory = DataFactory.createForClass(UserFactory);
|
|
95
|
+
|
|
96
|
+
// Generate 10 users
|
|
121
97
|
const users = factory.generate(10);
|
|
122
98
|
|
|
123
99
|
// Save to database
|
|
124
100
|
await this.userRepository.save(users);
|
|
101
|
+
|
|
102
|
+
console.log('โ
Seeded 10 users');
|
|
125
103
|
}
|
|
126
104
|
|
|
127
|
-
async drop(
|
|
128
|
-
//
|
|
105
|
+
async drop(): Promise<void> {
|
|
106
|
+
// Clear all users
|
|
129
107
|
await this.userRepository.delete({});
|
|
108
|
+
|
|
109
|
+
console.log('๐๏ธ Dropped all users');
|
|
130
110
|
}
|
|
131
111
|
}
|
|
132
112
|
```
|
|
133
113
|
|
|
134
|
-
###
|
|
114
|
+
### Step 4: Create Configuration File
|
|
115
|
+
|
|
116
|
+
Create `seeder.config.ts` in your **project root**:
|
|
135
117
|
|
|
136
118
|
```typescript
|
|
137
|
-
//
|
|
138
|
-
import { Module } from '@nestjs/common';
|
|
119
|
+
// seeder.config.ts
|
|
139
120
|
import { TypeOrmModule } from '@nestjs/typeorm';
|
|
140
|
-
import {
|
|
141
|
-
import {
|
|
142
|
-
import { UserSeeder } from './database/seeders/user.seeder';
|
|
121
|
+
import { User } from './src/entities/user.entity';
|
|
122
|
+
import { UserSeeder } from './src/seeders/user.seeder';
|
|
143
123
|
|
|
144
|
-
|
|
124
|
+
export default {
|
|
145
125
|
imports: [
|
|
126
|
+
// Database configuration
|
|
146
127
|
TypeOrmModule.forRoot({
|
|
147
128
|
type: 'postgres',
|
|
148
129
|
host: 'localhost',
|
|
@@ -153,244 +134,228 @@ import { UserSeeder } from './database/seeders/user.seeder';
|
|
|
153
134
|
entities: [User],
|
|
154
135
|
synchronize: true,
|
|
155
136
|
}),
|
|
137
|
+
|
|
138
|
+
// Register repositories
|
|
156
139
|
TypeOrmModule.forFeature([User]),
|
|
157
|
-
SeederModule.register({
|
|
158
|
-
seeders: [UserSeeder],
|
|
159
|
-
}),
|
|
160
140
|
],
|
|
161
|
-
|
|
162
|
-
|
|
141
|
+
|
|
142
|
+
// List seeders (run in order)
|
|
143
|
+
seeders: [UserSeeder],
|
|
144
|
+
};
|
|
163
145
|
```
|
|
164
146
|
|
|
165
|
-
###
|
|
147
|
+
### Step 5: Run Seeder
|
|
166
148
|
|
|
167
|
-
|
|
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
|
-
}
|
|
149
|
+
Add script to `package.json`:
|
|
181
150
|
|
|
182
|
-
|
|
151
|
+
```json
|
|
152
|
+
{
|
|
153
|
+
"scripts": {
|
|
154
|
+
"seed": "nest-seed -c seeder.config.ts"
|
|
155
|
+
}
|
|
156
|
+
}
|
|
183
157
|
```
|
|
184
158
|
|
|
185
159
|
Run it:
|
|
160
|
+
|
|
186
161
|
```bash
|
|
187
|
-
|
|
162
|
+
npm run seed
|
|
188
163
|
```
|
|
189
164
|
|
|
190
|
-
|
|
165
|
+
**That's it!** Your database is now seeded! ๐
|
|
191
166
|
|
|
192
|
-
|
|
167
|
+
## ๐ฅ๏ธ CLI Commands
|
|
193
168
|
|
|
194
|
-
|
|
169
|
+
### Basic Usage
|
|
195
170
|
|
|
196
|
-
```
|
|
197
|
-
|
|
171
|
+
```bash
|
|
172
|
+
# Run all seeders
|
|
173
|
+
nest-seed -c seeder.config.ts
|
|
198
174
|
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
})
|
|
208
|
-
export class AppModule {}
|
|
175
|
+
# Drop and reseed
|
|
176
|
+
nest-seed -c seeder.config.ts --refresh
|
|
177
|
+
|
|
178
|
+
# Run specific seeder
|
|
179
|
+
nest-seed -c seeder.config.ts --name UserSeeder
|
|
180
|
+
|
|
181
|
+
# Run multiple seeders
|
|
182
|
+
nest-seed -c seeder.config.ts --name UserSeeder ProductSeeder
|
|
209
183
|
```
|
|
210
184
|
|
|
211
|
-
###
|
|
185
|
+
### Available Options
|
|
212
186
|
|
|
213
|
-
|
|
187
|
+
| Option | Alias | Description | Default |
|
|
188
|
+
|--------|-------|-------------|---------|
|
|
189
|
+
| `--config` | `-c` | Path to configuration file | (required) |
|
|
190
|
+
| `--refresh` | `-r` | Drop data before seeding | `false` |
|
|
191
|
+
| `--name` | `-n` | Run specific seeder(s) | (all) |
|
|
192
|
+
| `--dummyData` | `-d` | Include dummy data flag | `false` |
|
|
193
|
+
| `--help` | `-h` | Show help | |
|
|
214
194
|
|
|
215
|
-
|
|
195
|
+
### Package.json Scripts
|
|
196
|
+
|
|
197
|
+
```json
|
|
198
|
+
{
|
|
199
|
+
"scripts": {
|
|
200
|
+
"seed": "nest-seed -c seeder.config.ts",
|
|
201
|
+
"seed:refresh": "nest-seed -c seeder.config.ts -r",
|
|
202
|
+
"seed:users": "nest-seed -c seeder.config.ts -n UserSeeder",
|
|
203
|
+
"seed:watch": "nodemon --watch src/seeders --ext ts --exec nest-seed -c seeder.config.ts"
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
## โ๏ธ Configuration
|
|
209
|
+
|
|
210
|
+
### TypeORM Example
|
|
216
211
|
|
|
217
212
|
```typescript
|
|
218
|
-
|
|
219
|
-
import {
|
|
213
|
+
// seeder.config.ts
|
|
214
|
+
import { TypeOrmModule } from '@nestjs/typeorm';
|
|
215
|
+
import { User, Post, Comment } from './src/entities';
|
|
216
|
+
import { UserSeeder, PostSeeder, CommentSeeder } from './src/seeders';
|
|
220
217
|
|
|
221
|
-
|
|
218
|
+
export default {
|
|
222
219
|
imports: [
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
220
|
+
TypeOrmModule.forRoot({
|
|
221
|
+
type: 'postgres',
|
|
222
|
+
host: process.env.DB_HOST || 'localhost',
|
|
223
|
+
port: parseInt(process.env.DB_PORT) || 5432,
|
|
224
|
+
username: process.env.DB_USER || 'postgres',
|
|
225
|
+
password: process.env.DB_PASSWORD || 'postgres',
|
|
226
|
+
database: process.env.DB_NAME || 'mydb',
|
|
227
|
+
entities: [User, Post, Comment],
|
|
228
|
+
synchronize: true,
|
|
232
229
|
}),
|
|
230
|
+
TypeOrmModule.forFeature([User, Post, Comment]),
|
|
233
231
|
],
|
|
234
|
-
|
|
235
|
-
|
|
232
|
+
seeders: [UserSeeder, PostSeeder, CommentSeeder],
|
|
233
|
+
};
|
|
236
234
|
```
|
|
237
235
|
|
|
238
|
-
|
|
236
|
+
### MongoDB/Mongoose Example
|
|
239
237
|
|
|
240
238
|
```typescript
|
|
241
|
-
|
|
242
|
-
import {
|
|
243
|
-
import {
|
|
239
|
+
// seeder.config.ts
|
|
240
|
+
import { MongooseModule } from '@nestjs/mongoose';
|
|
241
|
+
import { User, UserSchema } from './src/schemas/user.schema';
|
|
242
|
+
import { UserSeeder } from './src/seeders/user.seeder';
|
|
244
243
|
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
244
|
+
export default {
|
|
245
|
+
imports: [
|
|
246
|
+
MongooseModule.forRoot('mongodb://localhost/mydb'),
|
|
247
|
+
MongooseModule.forFeature([
|
|
248
|
+
{ name: User.name, schema: UserSchema }
|
|
249
|
+
]),
|
|
250
|
+
],
|
|
251
|
+
seeders: [UserSeeder],
|
|
252
|
+
};
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
### SQLite Example
|
|
256
|
+
|
|
257
|
+
```typescript
|
|
258
|
+
// seeder.config.ts
|
|
259
|
+
import { TypeOrmModule } from '@nestjs/typeorm';
|
|
260
|
+
import { User } from './src/entities/user.entity';
|
|
261
|
+
import { UserSeeder } from './src/seeders/user.seeder';
|
|
256
262
|
|
|
257
|
-
|
|
263
|
+
export default {
|
|
258
264
|
imports: [
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
265
|
+
TypeOrmModule.forRoot({
|
|
266
|
+
type: 'sqlite',
|
|
267
|
+
database: 'database.sqlite',
|
|
268
|
+
entities: [User],
|
|
269
|
+
synchronize: true,
|
|
263
270
|
}),
|
|
271
|
+
TypeOrmModule.forFeature([User]),
|
|
264
272
|
],
|
|
265
|
-
|
|
266
|
-
|
|
273
|
+
seeders: [UserSeeder],
|
|
274
|
+
};
|
|
267
275
|
```
|
|
268
276
|
|
|
269
|
-
##
|
|
270
|
-
|
|
271
|
-
### Basic Seeder
|
|
277
|
+
## ๐ญ Factories
|
|
272
278
|
|
|
273
|
-
|
|
279
|
+
### Basic Factory
|
|
274
280
|
|
|
275
281
|
```typescript
|
|
276
|
-
import {
|
|
277
|
-
import { Seeder, SeederServiceOptions } from '@ackplus/nest-seeder';
|
|
282
|
+
import { Factory } from '@ackplus/nest-seeder';
|
|
278
283
|
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
console.log('Seeding data...');
|
|
283
|
-
// Your seeding logic here
|
|
284
|
-
}
|
|
284
|
+
export class UserFactory {
|
|
285
|
+
@Factory((faker) => faker.person.fullName())
|
|
286
|
+
name: string;
|
|
285
287
|
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
}
|
|
288
|
+
@Factory((faker) => faker.internet.email())
|
|
289
|
+
email: string;
|
|
290
|
+
|
|
291
|
+
@Factory((faker) => faker.datatype.number({ min: 18, max: 80 }))
|
|
292
|
+
age: number;
|
|
290
293
|
}
|
|
291
294
|
```
|
|
292
295
|
|
|
293
|
-
### Using
|
|
296
|
+
### Using Factory
|
|
294
297
|
|
|
295
298
|
```typescript
|
|
296
|
-
import {
|
|
297
|
-
import {
|
|
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
|
-
) {}
|
|
299
|
+
import { DataFactory } from '@ackplus/nest-seeder';
|
|
300
|
+
import { UserFactory } from './user.factory';
|
|
309
301
|
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
// Generate different amounts based on options
|
|
314
|
-
const count = options.dummyData ? 100 : 10;
|
|
315
|
-
const users = factory.generate(count);
|
|
302
|
+
// Create factory
|
|
303
|
+
const factory = DataFactory.createForClass(UserFactory);
|
|
316
304
|
|
|
317
|
-
|
|
318
|
-
|
|
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
|
-
}
|
|
305
|
+
// Generate one object
|
|
306
|
+
const user = factory.generate(1)[0];
|
|
323
307
|
|
|
324
|
-
|
|
325
|
-
|
|
308
|
+
// Generate multiple objects
|
|
309
|
+
const users = factory.generate(10);
|
|
326
310
|
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
console.log('โ
Dropped all users');
|
|
330
|
-
}
|
|
331
|
-
}
|
|
311
|
+
// Generate with overrides
|
|
312
|
+
const admin = factory.generate(1, { role: 'admin' })[0];
|
|
332
313
|
```
|
|
333
314
|
|
|
334
|
-
###
|
|
315
|
+
### Factory with Relationships
|
|
335
316
|
|
|
336
317
|
```typescript
|
|
337
|
-
import {
|
|
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
|
-
) {}
|
|
318
|
+
import { Factory } from '@ackplus/nest-seeder';
|
|
350
319
|
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
320
|
+
export class PostFactory {
|
|
321
|
+
@Factory((faker) => faker.lorem.sentence())
|
|
322
|
+
title: string;
|
|
354
323
|
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
}
|
|
324
|
+
@Factory((faker) => faker.lorem.paragraphs(3))
|
|
325
|
+
content: string;
|
|
358
326
|
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
console.log('โ
Dropped all users');
|
|
362
|
-
}
|
|
327
|
+
// Will be set manually in seeder
|
|
328
|
+
authorId: number;
|
|
363
329
|
}
|
|
364
330
|
```
|
|
365
331
|
|
|
366
|
-
|
|
332
|
+
## ๐ฑ Seeders
|
|
333
|
+
|
|
334
|
+
### Basic Seeder
|
|
367
335
|
|
|
368
336
|
```typescript
|
|
369
337
|
import { Injectable } from '@nestjs/common';
|
|
370
|
-
import {
|
|
371
|
-
import {
|
|
338
|
+
import { InjectRepository } from '@nestjs/typeorm';
|
|
339
|
+
import { Repository } from 'typeorm';
|
|
340
|
+
import { Seeder, DataFactory } from '@ackplus/nest-seeder';
|
|
341
|
+
import { User } from '../entities/user.entity';
|
|
372
342
|
import { UserFactory } from '../factories/user.factory';
|
|
373
343
|
|
|
374
344
|
@Injectable()
|
|
375
345
|
export class UserSeeder implements Seeder {
|
|
376
|
-
constructor(
|
|
346
|
+
constructor(
|
|
347
|
+
@InjectRepository(User)
|
|
348
|
+
private readonly userRepository: Repository<User>,
|
|
349
|
+
) {}
|
|
377
350
|
|
|
378
|
-
async seed(
|
|
351
|
+
async seed(): Promise<void> {
|
|
379
352
|
const factory = DataFactory.createForClass(UserFactory);
|
|
380
353
|
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');
|
|
354
|
+
await this.userRepository.save(users);
|
|
389
355
|
}
|
|
390
356
|
|
|
391
|
-
async drop(
|
|
392
|
-
await this.
|
|
393
|
-
console.log('โ
Dropped all users');
|
|
357
|
+
async drop(): Promise<void> {
|
|
358
|
+
await this.userRepository.delete({});
|
|
394
359
|
}
|
|
395
360
|
}
|
|
396
361
|
```
|
|
@@ -401,662 +366,228 @@ export class UserSeeder implements Seeder {
|
|
|
401
366
|
import { Injectable } from '@nestjs/common';
|
|
402
367
|
import { InjectRepository } from '@nestjs/typeorm';
|
|
403
368
|
import { Repository } from 'typeorm';
|
|
404
|
-
import { Seeder,
|
|
405
|
-
import { Post } from '../entities/post.entity';
|
|
369
|
+
import { Seeder, DataFactory } from '@ackplus/nest-seeder';
|
|
406
370
|
import { User } from '../entities/user.entity';
|
|
371
|
+
import { Post } from '../entities/post.entity';
|
|
372
|
+
import { UserFactory } from '../factories/user.factory';
|
|
407
373
|
import { PostFactory } from '../factories/post.factory';
|
|
408
374
|
|
|
409
375
|
@Injectable()
|
|
410
376
|
export class PostSeeder implements Seeder {
|
|
411
377
|
constructor(
|
|
412
|
-
@InjectRepository(Post)
|
|
413
|
-
private readonly postRepository: Repository<Post>,
|
|
414
378
|
@InjectRepository(User)
|
|
415
379
|
private readonly userRepository: Repository<User>,
|
|
380
|
+
@InjectRepository(Post)
|
|
381
|
+
private readonly postRepository: Repository<Post>,
|
|
416
382
|
) {}
|
|
417
383
|
|
|
418
|
-
async seed(
|
|
384
|
+
async seed(): Promise<void> {
|
|
419
385
|
// Get existing users
|
|
420
386
|
const users = await this.userRepository.find();
|
|
421
387
|
|
|
422
388
|
if (users.length === 0) {
|
|
423
|
-
console.
|
|
389
|
+
console.log('โ ๏ธ No users found. Run UserSeeder first.');
|
|
424
390
|
return;
|
|
425
391
|
}
|
|
426
392
|
|
|
427
|
-
|
|
393
|
+
// Create posts for each user
|
|
394
|
+
const postFactory = DataFactory.createForClass(PostFactory);
|
|
428
395
|
|
|
429
|
-
// Generate 5 posts per user
|
|
430
396
|
for (const user of users) {
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
397
|
+
// Generate 3 posts per user
|
|
398
|
+
const posts = postFactory.generate(3).map(post => ({
|
|
399
|
+
...post,
|
|
400
|
+
author: user,
|
|
401
|
+
}));
|
|
402
|
+
|
|
434
403
|
await this.postRepository.save(posts);
|
|
435
404
|
}
|
|
436
|
-
|
|
437
|
-
console.log(`โ
Seeded ${users.length *
|
|
405
|
+
|
|
406
|
+
console.log(`โ
Seeded ${users.length * 3} posts`);
|
|
438
407
|
}
|
|
439
408
|
|
|
440
|
-
async drop(
|
|
409
|
+
async drop(): Promise<void> {
|
|
441
410
|
await this.postRepository.delete({});
|
|
442
|
-
console.log('โ
Dropped all posts');
|
|
443
411
|
}
|
|
444
412
|
}
|
|
445
413
|
```
|
|
446
414
|
|
|
447
|
-
|
|
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:
|
|
415
|
+
### Conditional Seeding
|
|
497
416
|
|
|
498
417
|
```typescript
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
firstName: string;
|
|
502
|
-
|
|
503
|
-
@Factory((faker) => faker.person.lastName())
|
|
504
|
-
lastName: string;
|
|
418
|
+
import { Injectable } from '@nestjs/common';
|
|
419
|
+
import { Seeder, SeederServiceOptions, DataFactory } from '@ackplus/nest-seeder';
|
|
505
420
|
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
421
|
+
@Injectable()
|
|
422
|
+
export class UserSeeder implements Seeder {
|
|
423
|
+
async seed(options?: SeederServiceOptions): Promise<void> {
|
|
424
|
+
const factory = DataFactory.createForClass(UserFactory);
|
|
425
|
+
|
|
426
|
+
// Seed more data if dummyData flag is set
|
|
427
|
+
const count = options?.dummyData ? 100 : 10;
|
|
428
|
+
const users = factory.generate(count);
|
|
429
|
+
|
|
430
|
+
await this.userRepository.save(users);
|
|
431
|
+
console.log(`โ
Seeded ${count} users`);
|
|
432
|
+
}
|
|
511
433
|
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
}, ['firstName', 'lastName'])
|
|
516
|
-
fullName: string;
|
|
434
|
+
async drop(): Promise<void> {
|
|
435
|
+
await this.userRepository.delete({});
|
|
436
|
+
}
|
|
517
437
|
}
|
|
518
438
|
```
|
|
519
439
|
|
|
520
|
-
|
|
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;
|
|
440
|
+
Run with dummy data:
|
|
552
441
|
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
}
|
|
442
|
+
```bash
|
|
443
|
+
nest-seed -c seeder.config.ts --dummyData
|
|
556
444
|
```
|
|
557
445
|
|
|
558
|
-
###
|
|
446
|
+
### MongoDB/Mongoose Seeder
|
|
559
447
|
|
|
560
448
|
```typescript
|
|
561
|
-
import {
|
|
562
|
-
import {
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
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
|
|
449
|
+
import { Injectable } from '@nestjs/common';
|
|
450
|
+
import { InjectModel } from '@nestjs/mongoose';
|
|
451
|
+
import { Model } from 'mongoose';
|
|
452
|
+
import { Seeder, DataFactory } from '@ackplus/nest-seeder';
|
|
453
|
+
import { User } from '../schemas/user.schema';
|
|
454
|
+
import { UserFactory } from '../factories/user.factory';
|
|
591
455
|
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
456
|
+
@Injectable()
|
|
457
|
+
export class UserSeeder implements Seeder {
|
|
458
|
+
constructor(
|
|
459
|
+
@InjectModel(User.name)
|
|
460
|
+
private readonly userModel: Model<User>,
|
|
461
|
+
) {}
|
|
595
462
|
|
|
596
|
-
|
|
463
|
+
async seed(): Promise<void> {
|
|
464
|
+
const factory = DataFactory.createForClass(UserFactory);
|
|
465
|
+
const users = factory.generate(10);
|
|
466
|
+
await this.userModel.insertMany(users);
|
|
467
|
+
}
|
|
597
468
|
|
|
598
|
-
|
|
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"
|
|
469
|
+
async drop(): Promise<void> {
|
|
470
|
+
await this.userModel.deleteMany({});
|
|
604
471
|
}
|
|
605
472
|
}
|
|
606
473
|
```
|
|
607
474
|
|
|
608
|
-
|
|
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
|
|
475
|
+
## ๐ฅ Advanced Examples
|
|
706
476
|
|
|
707
|
-
|
|
477
|
+
### Custom Providers in Config
|
|
708
478
|
|
|
709
479
|
```typescript
|
|
710
480
|
// seeder.config.ts
|
|
711
|
-
import {
|
|
481
|
+
import { CustomService } from './src/services/custom.service';
|
|
712
482
|
|
|
713
|
-
|
|
714
|
-
// Required: Array of seeder providers
|
|
715
|
-
seeders: [UserSeeder, PostSeeder, CommentSeeder],
|
|
716
|
-
|
|
717
|
-
// Optional: Modules to import (e.g., TypeORM, Mongoose)
|
|
483
|
+
export default {
|
|
718
484
|
imports: [
|
|
719
485
|
TypeOrmModule.forRoot({ /* ... */ }),
|
|
720
|
-
TypeOrmModule.forFeature([User
|
|
486
|
+
TypeOrmModule.forFeature([User]),
|
|
721
487
|
],
|
|
722
|
-
|
|
723
|
-
//
|
|
724
|
-
providers: [PrismaService, CustomService],
|
|
488
|
+
seeders: [UserSeeder],
|
|
489
|
+
providers: [CustomService], // Inject custom services
|
|
725
490
|
};
|
|
726
|
-
|
|
727
|
-
export default config;
|
|
728
491
|
```
|
|
729
492
|
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
### Using Seeder Function
|
|
493
|
+
### Environment-Based Configuration
|
|
733
494
|
|
|
734
495
|
```typescript
|
|
735
|
-
//
|
|
736
|
-
import
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
496
|
+
// seeder.config.ts
|
|
497
|
+
import * as dotenv from 'dotenv';
|
|
498
|
+
dotenv.config();
|
|
499
|
+
|
|
500
|
+
const isDev = process.env.NODE_ENV === 'development';
|
|
740
501
|
|
|
741
|
-
|
|
502
|
+
export default {
|
|
742
503
|
imports: [
|
|
743
504
|
TypeOrmModule.forRoot({
|
|
744
505
|
type: 'postgres',
|
|
745
|
-
host:
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
password: 'postgres',
|
|
749
|
-
database: 'mydb',
|
|
750
|
-
entities: [User],
|
|
751
|
-
synchronize: true,
|
|
506
|
+
host: process.env.DB_HOST,
|
|
507
|
+
database: isDev ? 'mydb_dev' : 'mydb_prod',
|
|
508
|
+
synchronize: isDev,
|
|
752
509
|
}),
|
|
753
|
-
TypeOrmModule.forFeature([User]),
|
|
510
|
+
TypeOrmModule.forFeature([User, Post]),
|
|
754
511
|
],
|
|
755
|
-
|
|
756
|
-
|
|
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();
|
|
512
|
+
seeders: isDev
|
|
513
|
+
? [UserSeeder, PostSeeder, TestDataSeeder]
|
|
514
|
+
: [UserSeeder, PostSeeder],
|
|
515
|
+
};
|
|
791
516
|
```
|
|
792
517
|
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
### Conditional Seeding
|
|
518
|
+
### Batch Insert for Performance
|
|
796
519
|
|
|
797
520
|
```typescript
|
|
798
521
|
@Injectable()
|
|
799
522
|
export class UserSeeder implements Seeder {
|
|
800
|
-
|
|
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
|
-
|
|
523
|
+
async seed(): Promise<void> {
|
|
813
524
|
const factory = DataFactory.createForClass(UserFactory);
|
|
525
|
+
const batchSize = 1000;
|
|
526
|
+
const totalRecords = 10000;
|
|
814
527
|
|
|
815
|
-
|
|
816
|
-
|
|
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
|
-
});
|
|
528
|
+
for (let i = 0; i < totalRecords; i += batchSize) {
|
|
529
|
+
const users = factory.generate(batchSize);
|
|
936
530
|
await this.userRepository.save(users);
|
|
531
|
+
console.log(`โ
Seeded ${Math.min(i + batchSize, totalRecords)}/${totalRecords} users`);
|
|
937
532
|
}
|
|
938
|
-
|
|
939
|
-
console.log('โ
Seeded 5 tenants with 10 users each');
|
|
940
533
|
}
|
|
941
534
|
|
|
942
|
-
async drop(
|
|
535
|
+
async drop(): Promise<void> {
|
|
943
536
|
await this.userRepository.delete({});
|
|
944
|
-
await this.tenantRepository.delete({});
|
|
945
537
|
}
|
|
946
538
|
}
|
|
947
539
|
```
|
|
948
540
|
|
|
949
541
|
## ๐ API Reference
|
|
950
542
|
|
|
951
|
-
###
|
|
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
|
|
543
|
+
### DataFactory
|
|
984
544
|
|
|
985
545
|
```typescript
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
546
|
+
class DataFactory {
|
|
547
|
+
// Create factory for a class
|
|
548
|
+
static createForClass<T>(factoryClass: new () => T): DataFactory<T>
|
|
549
|
+
|
|
550
|
+
// Generate instances
|
|
551
|
+
generate(count: number, override?: Partial<T>): T[]
|
|
990
552
|
}
|
|
991
553
|
```
|
|
992
554
|
|
|
993
|
-
###
|
|
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
|
|
555
|
+
### Seeder Interface
|
|
1000
556
|
|
|
1001
557
|
```typescript
|
|
1002
|
-
interface
|
|
1003
|
-
|
|
558
|
+
interface Seeder {
|
|
559
|
+
// Seed data into database
|
|
560
|
+
seed(options?: SeederServiceOptions): Promise<void>
|
|
561
|
+
|
|
562
|
+
// Drop/clear data from database
|
|
563
|
+
drop(options?: SeederServiceOptions): Promise<void>
|
|
1004
564
|
}
|
|
1005
565
|
```
|
|
1006
566
|
|
|
1007
567
|
### @Factory Decorator
|
|
1008
568
|
|
|
1009
569
|
```typescript
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
)
|
|
1014
|
-
```
|
|
1015
|
-
|
|
1016
|
-
**Parameters:**
|
|
1017
|
-
- `generator`: Function `(faker, ctx) => value` or static value
|
|
1018
|
-
- `dependsOn`: Array of property names this field depends on
|
|
570
|
+
// Simple factory
|
|
571
|
+
@Factory((faker) => faker.person.fullName())
|
|
572
|
+
name: string;
|
|
1019
573
|
|
|
1020
|
-
|
|
574
|
+
// With options
|
|
575
|
+
@Factory((faker) => faker.datatype.number({ min: 1, max: 100 }))
|
|
576
|
+
age: number;
|
|
1021
577
|
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
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
|
|
578
|
+
// Array values
|
|
579
|
+
@Factory((faker) => faker.helpers.arrayElement(['admin', 'user']))
|
|
580
|
+
role: string;
|
|
1044
581
|
```
|
|
1045
582
|
|
|
1046
|
-
|
|
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
|
|
583
|
+
### SeederServiceOptions
|
|
1058
584
|
|
|
1059
|
-
|
|
585
|
+
```typescript
|
|
586
|
+
interface SeederServiceOptions {
|
|
587
|
+
refresh?: boolean; // Drop before seeding
|
|
588
|
+
name?: string[]; // Run specific seeders
|
|
589
|
+
dummyData?: boolean; // Custom flag for your logic
|
|
590
|
+
}
|
|
1060
591
|
```
|
|
1061
592
|
|
|
1062
593
|
## ๐ค Contributing
|
|
@@ -1075,10 +606,10 @@ This project is licensed under the MIT License.
|
|
|
1075
606
|
|
|
1076
607
|
## ๐ฎ Support
|
|
1077
608
|
|
|
1078
|
-
If you have any questions or need help
|
|
1079
|
-
- Open an issue on GitHub
|
|
1080
|
-
- Check
|
|
1081
|
-
- Review
|
|
609
|
+
If you have any questions or need help:
|
|
610
|
+
- Open an issue on [GitHub](https://github.com/ackplus/nest-seeder/issues)
|
|
611
|
+
- Check the [examples](./examples/) directory
|
|
612
|
+
- Review the [Quick Start Guide](./QUICKSTART.md)
|
|
1082
613
|
|
|
1083
614
|
---
|
|
1084
615
|
|