@ackplus/nest-seeder 1.1.12 โ 1.1.15-beta.3
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 +126 -116
- package/README.md +354 -811
- package/dist/cli.js +68 -13
- package/dist/cli.js.map +1 -1
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +20 -21
- package/examples/post.seeder.example.ts +0 -65
- package/examples/product.factory.example.ts +0 -84
- package/examples/seed.script.example.ts +0 -32
- package/examples/seeder.config.example.ts +0 -50
- package/examples/user.factory.example.ts +0 -59
- package/examples/user.seeder.example.ts +0 -55
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,282 +89,285 @@ 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**:
|
|
117
|
+
|
|
118
|
+
> **Note:** The seeder configuration is independent and does not require importing your main `AppModule`.
|
|
135
119
|
|
|
136
120
|
```typescript
|
|
137
|
-
//
|
|
138
|
-
import { Module } from '@nestjs/common';
|
|
121
|
+
// seeder.config.ts
|
|
139
122
|
import { TypeOrmModule } from '@nestjs/typeorm';
|
|
140
|
-
import {
|
|
141
|
-
import { User } from './
|
|
142
|
-
import { UserSeeder } from './
|
|
123
|
+
import { ConfigModule, ConfigService } from '@nestjs/config';
|
|
124
|
+
import { User } from './src/entities/user.entity';
|
|
125
|
+
import { UserSeeder } from './src/seeders/user.seeder';
|
|
126
|
+
import * as dotenv from 'dotenv';
|
|
127
|
+
|
|
128
|
+
dotenv.config();
|
|
143
129
|
|
|
144
|
-
|
|
130
|
+
export default {
|
|
145
131
|
imports: [
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
host: 'localhost',
|
|
149
|
-
port: 5432,
|
|
150
|
-
username: 'postgres',
|
|
151
|
-
password: 'postgres',
|
|
152
|
-
database: 'mydb',
|
|
153
|
-
entities: [User],
|
|
154
|
-
synchronize: true,
|
|
132
|
+
ConfigModule.forRoot({
|
|
133
|
+
isGlobal: true,
|
|
155
134
|
}),
|
|
156
|
-
TypeOrmModule.
|
|
157
|
-
|
|
158
|
-
|
|
135
|
+
TypeOrmModule.forRootAsync({
|
|
136
|
+
imports: [ConfigModule],
|
|
137
|
+
inject: [ConfigService],
|
|
138
|
+
useFactory: (config: ConfigService) => ({
|
|
139
|
+
type: 'postgres',
|
|
140
|
+
host: config.get<string>('DB_HOST'),
|
|
141
|
+
port: config.get<number>('DB_PORT'),
|
|
142
|
+
username: config.get<string>('DB_USERNAME'),
|
|
143
|
+
password: config.get<string>('DB_PASSWORD'),
|
|
144
|
+
database: config.get<string>('DB_DATABASE'),
|
|
145
|
+
entities: [User],
|
|
146
|
+
synchronize: true,
|
|
147
|
+
}),
|
|
159
148
|
}),
|
|
149
|
+
TypeOrmModule.forFeature([User]),
|
|
160
150
|
],
|
|
161
|
-
|
|
162
|
-
|
|
151
|
+
seeders: [UserSeeder],
|
|
152
|
+
};
|
|
163
153
|
```
|
|
164
154
|
|
|
165
|
-
###
|
|
155
|
+
### Step 5: Run Seeder
|
|
166
156
|
|
|
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
|
-
}
|
|
157
|
+
Add script to `package.json`:
|
|
181
158
|
|
|
182
|
-
|
|
159
|
+
```json
|
|
160
|
+
{
|
|
161
|
+
"scripts": {
|
|
162
|
+
"seed": "node -r ts-node/register -r tsconfig-paths/register ./node_modules/@ackplus/nest-seeder/dist/cli.js -c ./seeder.config.ts"
|
|
163
|
+
}
|
|
164
|
+
}
|
|
183
165
|
```
|
|
184
166
|
|
|
185
167
|
Run it:
|
|
168
|
+
|
|
186
169
|
```bash
|
|
187
|
-
|
|
170
|
+
npm run seed
|
|
188
171
|
```
|
|
189
172
|
|
|
190
|
-
|
|
173
|
+
**That's it!** Your database is now seeded! ๐
|
|
191
174
|
|
|
192
|
-
|
|
175
|
+
## ๐ฅ๏ธ CLI Commands
|
|
193
176
|
|
|
194
|
-
|
|
177
|
+
Since you are running the seeder via a package script, you can pass arguments using `--`.
|
|
195
178
|
|
|
196
|
-
|
|
197
|
-
import { SeederModule } from '@ackplus/nest-seeder';
|
|
179
|
+
### Basic Usage
|
|
198
180
|
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
181
|
+
```bash
|
|
182
|
+
# Run all seeders
|
|
183
|
+
npm run seed
|
|
184
|
+
|
|
185
|
+
# Drop and reseed
|
|
186
|
+
npm run seed -- --refresh
|
|
187
|
+
|
|
188
|
+
# Run specific seeder
|
|
189
|
+
npm run seed -- --name UserSeeder
|
|
190
|
+
|
|
191
|
+
# Run multiple seeders
|
|
192
|
+
npm run seed -- --name UserSeeder ProductSeeder
|
|
209
193
|
```
|
|
210
194
|
|
|
211
|
-
###
|
|
195
|
+
### Available Options
|
|
212
196
|
|
|
213
|
-
|
|
197
|
+
| Option | Alias | Description | Default |
|
|
198
|
+
|--------|-------|-------------|---------|
|
|
199
|
+
| `--config` | `-c` | Path to configuration file | (required) |
|
|
200
|
+
| `--refresh` | `-r` | Drop data before seeding | `false` |
|
|
201
|
+
| `--name` | `-n` | Run specific seeder(s) | (all) |
|
|
202
|
+
| `--dummyData` | `-d` | Include dummy data flag | `false` |
|
|
203
|
+
| `--help` | `-h` | Show help | |
|
|
214
204
|
|
|
215
|
-
|
|
205
|
+
### Package.json Scripts
|
|
206
|
+
|
|
207
|
+
You can also define specific scripts for convenience:
|
|
208
|
+
|
|
209
|
+
```json
|
|
210
|
+
{
|
|
211
|
+
"scripts": {
|
|
212
|
+
"seed": "node -r ts-node/register -r tsconfig-paths/register ./node_modules/@ackplus/nest-seeder/dist/cli.js -c ./seeder.config.ts",
|
|
213
|
+
"seed:refresh": "npm run seed -- --refresh",
|
|
214
|
+
"seed:users": "npm run seed -- --name UserSeeder",
|
|
215
|
+
"seed:watch": "nodemon --watch src/seeders --ext ts --exec \"npm run seed\""
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
## โ๏ธ Configuration
|
|
221
|
+
|
|
222
|
+
### TypeORM Example
|
|
216
223
|
|
|
217
224
|
```typescript
|
|
218
|
-
|
|
219
|
-
import {
|
|
225
|
+
// seeder.config.ts
|
|
226
|
+
import { TypeOrmModule } from '@nestjs/typeorm';
|
|
227
|
+
import { User, Post, Comment } from './src/entities';
|
|
228
|
+
import { UserSeeder, PostSeeder, CommentSeeder } from './src/seeders';
|
|
220
229
|
|
|
221
|
-
|
|
230
|
+
export default {
|
|
222
231
|
imports: [
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
+
TypeOrmModule.forRoot({
|
|
233
|
+
type: 'postgres',
|
|
234
|
+
host: process.env.DB_HOST || 'localhost',
|
|
235
|
+
port: parseInt(process.env.DB_PORT) || 5432,
|
|
236
|
+
username: process.env.DB_USER || 'postgres',
|
|
237
|
+
password: process.env.DB_PASSWORD || 'postgres',
|
|
238
|
+
database: process.env.DB_NAME || 'mydb',
|
|
239
|
+
entities: [User, Post, Comment],
|
|
240
|
+
synchronize: true,
|
|
232
241
|
}),
|
|
242
|
+
TypeOrmModule.forFeature([User, Post, Comment]),
|
|
233
243
|
],
|
|
234
|
-
|
|
235
|
-
|
|
244
|
+
seeders: [UserSeeder, PostSeeder, CommentSeeder],
|
|
245
|
+
};
|
|
236
246
|
```
|
|
237
247
|
|
|
238
|
-
|
|
248
|
+
### MongoDB/Mongoose Example
|
|
239
249
|
|
|
240
250
|
```typescript
|
|
241
|
-
|
|
242
|
-
import {
|
|
243
|
-
import {
|
|
251
|
+
// seeder.config.ts
|
|
252
|
+
import { MongooseModule } from '@nestjs/mongoose';
|
|
253
|
+
import { User, UserSchema } from './src/schemas/user.schema';
|
|
254
|
+
import { UserSeeder } from './src/seeders/user.seeder';
|
|
244
255
|
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
+
export default {
|
|
257
|
+
imports: [
|
|
258
|
+
MongooseModule.forRoot('mongodb://localhost/mydb'),
|
|
259
|
+
MongooseModule.forFeature([
|
|
260
|
+
{ name: User.name, schema: UserSchema }
|
|
261
|
+
]),
|
|
262
|
+
],
|
|
263
|
+
seeders: [UserSeeder],
|
|
264
|
+
};
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
### SQLite Example
|
|
268
|
+
|
|
269
|
+
```typescript
|
|
270
|
+
// seeder.config.ts
|
|
271
|
+
import { TypeOrmModule } from '@nestjs/typeorm';
|
|
272
|
+
import { User } from './src/entities/user.entity';
|
|
273
|
+
import { UserSeeder } from './src/seeders/user.seeder';
|
|
256
274
|
|
|
257
|
-
|
|
275
|
+
export default {
|
|
258
276
|
imports: [
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
277
|
+
TypeOrmModule.forRoot({
|
|
278
|
+
type: 'sqlite',
|
|
279
|
+
database: 'database.sqlite',
|
|
280
|
+
entities: [User],
|
|
281
|
+
synchronize: true,
|
|
263
282
|
}),
|
|
283
|
+
TypeOrmModule.forFeature([User]),
|
|
264
284
|
],
|
|
265
|
-
|
|
266
|
-
|
|
285
|
+
seeders: [UserSeeder],
|
|
286
|
+
};
|
|
267
287
|
```
|
|
268
288
|
|
|
269
|
-
##
|
|
270
|
-
|
|
271
|
-
### Basic Seeder
|
|
289
|
+
## ๐ญ Factories
|
|
272
290
|
|
|
273
|
-
|
|
291
|
+
### Basic Factory
|
|
274
292
|
|
|
275
293
|
```typescript
|
|
276
|
-
import {
|
|
277
|
-
import { Seeder, SeederServiceOptions } from '@ackplus/nest-seeder';
|
|
294
|
+
import { Factory } from '@ackplus/nest-seeder';
|
|
278
295
|
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
console.log('Seeding data...');
|
|
283
|
-
// Your seeding logic here
|
|
284
|
-
}
|
|
296
|
+
export class UserFactory {
|
|
297
|
+
@Factory((faker) => faker.person.fullName())
|
|
298
|
+
name: string;
|
|
285
299
|
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
}
|
|
300
|
+
@Factory((faker) => faker.internet.email())
|
|
301
|
+
email: string;
|
|
302
|
+
|
|
303
|
+
@Factory((faker) => faker.datatype.number({ min: 18, max: 80 }))
|
|
304
|
+
age: number;
|
|
290
305
|
}
|
|
291
306
|
```
|
|
292
307
|
|
|
293
|
-
### Using
|
|
308
|
+
### Using Factory
|
|
294
309
|
|
|
295
310
|
```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
|
-
) {}
|
|
311
|
+
import { DataFactory } from '@ackplus/nest-seeder';
|
|
312
|
+
import { UserFactory } from './user.factory';
|
|
309
313
|
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
// Generate different amounts based on options
|
|
314
|
-
const count = options.dummyData ? 100 : 10;
|
|
315
|
-
const users = factory.generate(count);
|
|
314
|
+
// Create factory
|
|
315
|
+
const factory = DataFactory.createForClass(UserFactory);
|
|
316
316
|
|
|
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
|
-
}
|
|
317
|
+
// Generate one object
|
|
318
|
+
const user = factory.generate(1)[0];
|
|
323
319
|
|
|
324
|
-
|
|
325
|
-
|
|
320
|
+
// Generate multiple objects
|
|
321
|
+
const users = factory.generate(10);
|
|
326
322
|
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
console.log('โ
Dropped all users');
|
|
330
|
-
}
|
|
331
|
-
}
|
|
323
|
+
// Generate with overrides
|
|
324
|
+
const admin = factory.generate(1, { role: 'admin' })[0];
|
|
332
325
|
```
|
|
333
326
|
|
|
334
|
-
###
|
|
327
|
+
### Factory with Relationships
|
|
335
328
|
|
|
336
329
|
```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
|
-
) {}
|
|
330
|
+
import { Factory } from '@ackplus/nest-seeder';
|
|
350
331
|
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
332
|
+
export class PostFactory {
|
|
333
|
+
@Factory((faker) => faker.lorem.sentence())
|
|
334
|
+
title: string;
|
|
354
335
|
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
}
|
|
336
|
+
@Factory((faker) => faker.lorem.paragraphs(3))
|
|
337
|
+
content: string;
|
|
358
338
|
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
console.log('โ
Dropped all users');
|
|
362
|
-
}
|
|
339
|
+
// Will be set manually in seeder
|
|
340
|
+
authorId: number;
|
|
363
341
|
}
|
|
364
342
|
```
|
|
365
343
|
|
|
366
|
-
|
|
344
|
+
## ๐ฑ Seeders
|
|
345
|
+
|
|
346
|
+
### Basic Seeder
|
|
367
347
|
|
|
368
348
|
```typescript
|
|
369
349
|
import { Injectable } from '@nestjs/common';
|
|
370
|
-
import {
|
|
371
|
-
import {
|
|
350
|
+
import { InjectRepository } from '@nestjs/typeorm';
|
|
351
|
+
import { Repository } from 'typeorm';
|
|
352
|
+
import { Seeder, DataFactory } from '@ackplus/nest-seeder';
|
|
353
|
+
import { User } from '../entities/user.entity';
|
|
372
354
|
import { UserFactory } from '../factories/user.factory';
|
|
373
355
|
|
|
374
356
|
@Injectable()
|
|
375
357
|
export class UserSeeder implements Seeder {
|
|
376
|
-
constructor(
|
|
358
|
+
constructor(
|
|
359
|
+
@InjectRepository(User)
|
|
360
|
+
private readonly userRepository: Repository<User>,
|
|
361
|
+
) {}
|
|
377
362
|
|
|
378
|
-
async seed(
|
|
363
|
+
async seed(): Promise<void> {
|
|
379
364
|
const factory = DataFactory.createForClass(UserFactory);
|
|
380
365
|
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');
|
|
366
|
+
await this.userRepository.save(users);
|
|
389
367
|
}
|
|
390
368
|
|
|
391
|
-
async drop(
|
|
392
|
-
await this.
|
|
393
|
-
console.log('โ
Dropped all users');
|
|
369
|
+
async drop(): Promise<void> {
|
|
370
|
+
await this.userRepository.delete({});
|
|
394
371
|
}
|
|
395
372
|
}
|
|
396
373
|
```
|
|
@@ -401,662 +378,228 @@ export class UserSeeder implements Seeder {
|
|
|
401
378
|
import { Injectable } from '@nestjs/common';
|
|
402
379
|
import { InjectRepository } from '@nestjs/typeorm';
|
|
403
380
|
import { Repository } from 'typeorm';
|
|
404
|
-
import { Seeder,
|
|
405
|
-
import { Post } from '../entities/post.entity';
|
|
381
|
+
import { Seeder, DataFactory } from '@ackplus/nest-seeder';
|
|
406
382
|
import { User } from '../entities/user.entity';
|
|
383
|
+
import { Post } from '../entities/post.entity';
|
|
384
|
+
import { UserFactory } from '../factories/user.factory';
|
|
407
385
|
import { PostFactory } from '../factories/post.factory';
|
|
408
386
|
|
|
409
387
|
@Injectable()
|
|
410
388
|
export class PostSeeder implements Seeder {
|
|
411
389
|
constructor(
|
|
412
|
-
@InjectRepository(Post)
|
|
413
|
-
private readonly postRepository: Repository<Post>,
|
|
414
390
|
@InjectRepository(User)
|
|
415
391
|
private readonly userRepository: Repository<User>,
|
|
392
|
+
@InjectRepository(Post)
|
|
393
|
+
private readonly postRepository: Repository<Post>,
|
|
416
394
|
) {}
|
|
417
395
|
|
|
418
|
-
async seed(
|
|
396
|
+
async seed(): Promise<void> {
|
|
419
397
|
// Get existing users
|
|
420
398
|
const users = await this.userRepository.find();
|
|
421
399
|
|
|
422
400
|
if (users.length === 0) {
|
|
423
|
-
console.
|
|
401
|
+
console.log('โ ๏ธ No users found. Run UserSeeder first.');
|
|
424
402
|
return;
|
|
425
403
|
}
|
|
426
404
|
|
|
427
|
-
|
|
405
|
+
// Create posts for each user
|
|
406
|
+
const postFactory = DataFactory.createForClass(PostFactory);
|
|
428
407
|
|
|
429
|
-
// Generate 5 posts per user
|
|
430
408
|
for (const user of users) {
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
409
|
+
// Generate 3 posts per user
|
|
410
|
+
const posts = postFactory.generate(3).map(post => ({
|
|
411
|
+
...post,
|
|
412
|
+
author: user,
|
|
413
|
+
}));
|
|
414
|
+
|
|
434
415
|
await this.postRepository.save(posts);
|
|
435
416
|
}
|
|
436
|
-
|
|
437
|
-
console.log(`โ
Seeded ${users.length *
|
|
417
|
+
|
|
418
|
+
console.log(`โ
Seeded ${users.length * 3} posts`);
|
|
438
419
|
}
|
|
439
420
|
|
|
440
|
-
async drop(
|
|
421
|
+
async drop(): Promise<void> {
|
|
441
422
|
await this.postRepository.delete({});
|
|
442
|
-
console.log('โ
Dropped all posts');
|
|
443
423
|
}
|
|
444
424
|
}
|
|
445
425
|
```
|
|
446
426
|
|
|
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:
|
|
427
|
+
### Conditional Seeding
|
|
497
428
|
|
|
498
429
|
```typescript
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
firstName: string;
|
|
502
|
-
|
|
503
|
-
@Factory((faker) => faker.person.lastName())
|
|
504
|
-
lastName: string;
|
|
430
|
+
import { Injectable } from '@nestjs/common';
|
|
431
|
+
import { Seeder, SeederServiceOptions, DataFactory } from '@ackplus/nest-seeder';
|
|
505
432
|
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
433
|
+
@Injectable()
|
|
434
|
+
export class UserSeeder implements Seeder {
|
|
435
|
+
async seed(options?: SeederServiceOptions): Promise<void> {
|
|
436
|
+
const factory = DataFactory.createForClass(UserFactory);
|
|
437
|
+
|
|
438
|
+
// Seed more data if dummyData flag is set
|
|
439
|
+
const count = options?.dummyData ? 100 : 10;
|
|
440
|
+
const users = factory.generate(count);
|
|
441
|
+
|
|
442
|
+
await this.userRepository.save(users);
|
|
443
|
+
console.log(`โ
Seeded ${count} users`);
|
|
444
|
+
}
|
|
511
445
|
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
}, ['firstName', 'lastName'])
|
|
516
|
-
fullName: string;
|
|
446
|
+
async drop(): Promise<void> {
|
|
447
|
+
await this.userRepository.delete({});
|
|
448
|
+
}
|
|
517
449
|
}
|
|
518
450
|
```
|
|
519
451
|
|
|
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;
|
|
452
|
+
Run with dummy data:
|
|
540
453
|
|
|
541
|
-
|
|
542
|
-
|
|
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
|
-
}
|
|
454
|
+
```bash
|
|
455
|
+
nest-seed -c seeder.config.ts --dummyData
|
|
556
456
|
```
|
|
557
457
|
|
|
558
|
-
###
|
|
458
|
+
### MongoDB/Mongoose Seeder
|
|
559
459
|
|
|
560
460
|
```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
|
|
461
|
+
import { Injectable } from '@nestjs/common';
|
|
462
|
+
import { InjectModel } from '@nestjs/mongoose';
|
|
463
|
+
import { Model } from 'mongoose';
|
|
464
|
+
import { Seeder, DataFactory } from '@ackplus/nest-seeder';
|
|
465
|
+
import { User } from '../schemas/user.schema';
|
|
466
|
+
import { UserFactory } from '../factories/user.factory';
|
|
591
467
|
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
468
|
+
@Injectable()
|
|
469
|
+
export class UserSeeder implements Seeder {
|
|
470
|
+
constructor(
|
|
471
|
+
@InjectModel(User.name)
|
|
472
|
+
private readonly userModel: Model<User>,
|
|
473
|
+
) {}
|
|
595
474
|
|
|
596
|
-
|
|
475
|
+
async seed(): Promise<void> {
|
|
476
|
+
const factory = DataFactory.createForClass(UserFactory);
|
|
477
|
+
const users = factory.generate(10);
|
|
478
|
+
await this.userModel.insertMany(users);
|
|
479
|
+
}
|
|
597
480
|
|
|
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"
|
|
481
|
+
async drop(): Promise<void> {
|
|
482
|
+
await this.userModel.deleteMany({});
|
|
604
483
|
}
|
|
605
484
|
}
|
|
606
485
|
```
|
|
607
486
|
|
|
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
|
|
487
|
+
## ๐ฅ Advanced Examples
|
|
706
488
|
|
|
707
|
-
|
|
489
|
+
### Custom Providers in Config
|
|
708
490
|
|
|
709
491
|
```typescript
|
|
710
492
|
// seeder.config.ts
|
|
711
|
-
import {
|
|
493
|
+
import { CustomService } from './src/services/custom.service';
|
|
712
494
|
|
|
713
|
-
|
|
714
|
-
// Required: Array of seeder providers
|
|
715
|
-
seeders: [UserSeeder, PostSeeder, CommentSeeder],
|
|
716
|
-
|
|
717
|
-
// Optional: Modules to import (e.g., TypeORM, Mongoose)
|
|
495
|
+
export default {
|
|
718
496
|
imports: [
|
|
719
497
|
TypeOrmModule.forRoot({ /* ... */ }),
|
|
720
|
-
TypeOrmModule.forFeature([User
|
|
498
|
+
TypeOrmModule.forFeature([User]),
|
|
721
499
|
],
|
|
722
|
-
|
|
723
|
-
//
|
|
724
|
-
providers: [PrismaService, CustomService],
|
|
500
|
+
seeders: [UserSeeder],
|
|
501
|
+
providers: [CustomService], // Inject custom services
|
|
725
502
|
};
|
|
726
|
-
|
|
727
|
-
export default config;
|
|
728
503
|
```
|
|
729
504
|
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
### Using Seeder Function
|
|
505
|
+
### Environment-Based Configuration
|
|
733
506
|
|
|
734
507
|
```typescript
|
|
735
|
-
//
|
|
736
|
-
import
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
508
|
+
// seeder.config.ts
|
|
509
|
+
import * as dotenv from 'dotenv';
|
|
510
|
+
dotenv.config();
|
|
511
|
+
|
|
512
|
+
const isDev = process.env.NODE_ENV === 'development';
|
|
740
513
|
|
|
741
|
-
|
|
514
|
+
export default {
|
|
742
515
|
imports: [
|
|
743
516
|
TypeOrmModule.forRoot({
|
|
744
517
|
type: 'postgres',
|
|
745
|
-
host:
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
password: 'postgres',
|
|
749
|
-
database: 'mydb',
|
|
750
|
-
entities: [User],
|
|
751
|
-
synchronize: true,
|
|
518
|
+
host: process.env.DB_HOST,
|
|
519
|
+
database: isDev ? 'mydb_dev' : 'mydb_prod',
|
|
520
|
+
synchronize: isDev,
|
|
752
521
|
}),
|
|
753
|
-
TypeOrmModule.forFeature([User]),
|
|
522
|
+
TypeOrmModule.forFeature([User, Post]),
|
|
754
523
|
],
|
|
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();
|
|
524
|
+
seeders: isDev
|
|
525
|
+
? [UserSeeder, PostSeeder, TestDataSeeder]
|
|
526
|
+
: [UserSeeder, PostSeeder],
|
|
527
|
+
};
|
|
791
528
|
```
|
|
792
529
|
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
### Conditional Seeding
|
|
530
|
+
### Batch Insert for Performance
|
|
796
531
|
|
|
797
532
|
```typescript
|
|
798
533
|
@Injectable()
|
|
799
534
|
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
|
-
|
|
535
|
+
async seed(): Promise<void> {
|
|
813
536
|
const factory = DataFactory.createForClass(UserFactory);
|
|
537
|
+
const batchSize = 1000;
|
|
538
|
+
const totalRecords = 10000;
|
|
814
539
|
|
|
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
|
-
});
|
|
540
|
+
for (let i = 0; i < totalRecords; i += batchSize) {
|
|
541
|
+
const users = factory.generate(batchSize);
|
|
936
542
|
await this.userRepository.save(users);
|
|
543
|
+
console.log(`โ
Seeded ${Math.min(i + batchSize, totalRecords)}/${totalRecords} users`);
|
|
937
544
|
}
|
|
938
|
-
|
|
939
|
-
console.log('โ
Seeded 5 tenants with 10 users each');
|
|
940
545
|
}
|
|
941
546
|
|
|
942
|
-
async drop(
|
|
547
|
+
async drop(): Promise<void> {
|
|
943
548
|
await this.userRepository.delete({});
|
|
944
|
-
await this.tenantRepository.delete({});
|
|
945
549
|
}
|
|
946
550
|
}
|
|
947
551
|
```
|
|
948
552
|
|
|
949
553
|
## ๐ API Reference
|
|
950
554
|
|
|
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
|
|
555
|
+
### DataFactory
|
|
984
556
|
|
|
985
557
|
```typescript
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
558
|
+
class DataFactory {
|
|
559
|
+
// Create factory for a class
|
|
560
|
+
static createForClass<T>(factoryClass: new () => T): DataFactory<T>
|
|
561
|
+
|
|
562
|
+
// Generate instances
|
|
563
|
+
generate(count: number, override?: Partial<T>): T[]
|
|
990
564
|
}
|
|
991
565
|
```
|
|
992
566
|
|
|
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
|
|
567
|
+
### Seeder Interface
|
|
1000
568
|
|
|
1001
569
|
```typescript
|
|
1002
|
-
interface
|
|
1003
|
-
|
|
570
|
+
interface Seeder {
|
|
571
|
+
// Seed data into database
|
|
572
|
+
seed(options?: SeederServiceOptions): Promise<void>
|
|
573
|
+
|
|
574
|
+
// Drop/clear data from database
|
|
575
|
+
drop(options?: SeederServiceOptions): Promise<void>
|
|
1004
576
|
}
|
|
1005
577
|
```
|
|
1006
578
|
|
|
1007
579
|
### @Factory Decorator
|
|
1008
580
|
|
|
1009
581
|
```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
|
|
582
|
+
// Simple factory
|
|
583
|
+
@Factory((faker) => faker.person.fullName())
|
|
584
|
+
name: string;
|
|
1019
585
|
|
|
1020
|
-
|
|
586
|
+
// With options
|
|
587
|
+
@Factory((faker) => faker.datatype.number({ min: 1, max: 100 }))
|
|
588
|
+
age: number;
|
|
1021
589
|
|
|
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
|
|
590
|
+
// Array values
|
|
591
|
+
@Factory((faker) => faker.helpers.arrayElement(['admin', 'user']))
|
|
592
|
+
role: string;
|
|
1044
593
|
```
|
|
1045
594
|
|
|
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
|
|
595
|
+
### SeederServiceOptions
|
|
1058
596
|
|
|
1059
|
-
|
|
597
|
+
```typescript
|
|
598
|
+
interface SeederServiceOptions {
|
|
599
|
+
refresh?: boolean; // Drop before seeding
|
|
600
|
+
name?: string[]; // Run specific seeders
|
|
601
|
+
dummyData?: boolean; // Custom flag for your logic
|
|
602
|
+
}
|
|
1060
603
|
```
|
|
1061
604
|
|
|
1062
605
|
## ๐ค Contributing
|
|
@@ -1075,10 +618,10 @@ This project is licensed under the MIT License.
|
|
|
1075
618
|
|
|
1076
619
|
## ๐ฎ Support
|
|
1077
620
|
|
|
1078
|
-
If you have any questions or need help
|
|
1079
|
-
- Open an issue on GitHub
|
|
1080
|
-
- Check
|
|
1081
|
-
- Review
|
|
621
|
+
If you have any questions or need help:
|
|
622
|
+
- Open an issue on [GitHub](https://github.com/ackplus/nest-seeder/issues)
|
|
623
|
+
- Check the [examples](./examples/) directory
|
|
624
|
+
- Review the [Quick Start Guide](./QUICKSTART.md)
|
|
1082
625
|
|
|
1083
626
|
---
|
|
1084
627
|
|