nestjs+typeormでダミーデータを投入する方法

いろいろな記事を探してみたが、わかりやすくまとまっているnestjs + typeormでのダミーデータ投入方法の記事がなかった（見つけられなかった）ので、備忘録として残しておきます。

## 結論

seeder/role.seeder.ts
```ts
import { DataSource } from 'typeorm';
import { Seeder } from 'typeorm-extension';
import { Role } from '../user/entities/role.entity';

export class RoleSeeder implements Seeder {
  public async run(dataSource: DataSource): Promise<void> {
    const roleRepository = dataSource.getRepository(Role);

    const rolesList = [
      { roleName: 'admin' },
      { roleName: 'user' },
      { roleName: 'guest' },
    ];

    for (const roleData of rolesList) {
      const existingRole = await roleRepository.findOne({
        where: { roleName: roleData.roleName },
      });

      if (!existingRole) {
        const role = roleRepository.create(roleData);
        await roleRepository.save(role);
      }
    }
  }
}
```

seeder/seed.helper.ts
```ts
// src/seeder/seed.helper.ts
import { DataSource } from 'typeorm';
import { NestFactory } from '@nestjs/core';
import { AppModule } from '../app.module';
import { RoleSeeder } from './role.seeder';

async function runSeeder() {
  const app = await NestFactory.create(AppModule);
  const dataSource = app.get(DataSource);

  // ここにどんどんシーダーを追加していく
  const roleSeeder = new RoleSeeder();
  await roleSeeder.run(dataSource); // シーダーの呼び出し
  // ここまで

  await app.close();
}

runSeeder();
```

package.json
```json
{
  "scripts": {
    "data:sync": "ts-node -r tsconfig-paths/register ./src/seeder/seed.helper.ts",
  }
}
```

## ポイント

typeorm-extensionを使ったダミーデータの作成は、以下のポイントを抑えると楽かなと思います。

- DataSourceでリポジトリの取得
- NestFactoryを使うと凄い楽
- シーダー追加するのも楽

一つずつ詳細に解説します

### DataSourceでリポジトリの取得

RoleSeederで、以下のようなコードが書いてあるのですが…

```ts
  public async run(dataSource: DataSource): Promise<void> {
    const roleRepository = dataSource.getRepository(Role);
```

getRepositoryメソッドでentityクラスを指定するだけで簡単に取ってきてくれます。便利。

### NestFactoryを使うと凄い楽

基本的に、シーダーを使うときって、データベースの設定とかを引っ張ってくる必要があるのですが、今回の場合、AppModuleにTypeOrm.forRootで以下のように定義しているので、AppModuleをNestFactory.createメソッドに渡すだけでセットアップが完了してしまいます。

app.module
```ts
import { ConfigModule, ConfigService } from '@nestjs/config';
import { TypeOrmModule } from '@nestjs/typeorm';
// その他のインポート部分は省略

@Module({
  imports: [
    ConfigModule.forRoot({
      envFilePath: `.env.${process.env.NODE_ENV || 'development'}.local`,
      isGlobal: true,
    }),
    TypeOrmModule.forRootAsync({
      imports: [ConfigModule],
      useFactory: (configService: ConfigService) => ({
        type: configService.get<'mysql' | 'postgres'>('DB_TYPE'),
        host: configService.get<string>('DB_HOST'),
        port: configService.get<number>('DB_PORT'),
        username: configService.get<string>('DB_USERNAME'),
        password: configService.get<string>('DB_PASSWORD'),
        database: configService.get<string>('DB_DATABASE'),
        entities: [__dirname + '/**/*.entity{.ts,.js}'],
        synchronize: true,
      }),
      inject: [ConfigService],
    }),
    // その他のコードは省略
  ],
})
```

このようにapp.module.tsを設定している場合、seed.helper.tsで以下のように書くだけ。

```ts
  const app = await NestFactory.create(AppModule);
```

なんと楽なんでしょう。

その他、Factoryを用いてランダムなダミーデータの生成もできるのですが、こちらは余力があったら追記したいと思います。

### シーダー追加するのも楽

新たにシーダーを作った場合、以下の部分にどんどん足していけばいいだけです。

```ts
  // ここにどんどんシーダーを追加していく
  const roleSeeder = new RoleSeeder();
  await roleSeeder.run(dataSource); // シーダーの呼び出し

  // 例えば新たにuserのシーダー作った場合、以下のように追加する
  const userSeeder = new UserSeeder();
  await userSeeder.run(dataSource);
```

じゃんじゃんシーダーを追加していきましょう。

## 多分もっといい方法あるはず…

こんな記事を書いてみたのですが、これが正解とは思ってないですし、もっといい方法があるはずなので、いい方法知っていたら教えてください。