Nest.js でサーバ起動時にレコードを dump する
背景
Cykinso の 研究&データ技術開発 の山﨑です。
Nest.js で作成している API サーバを本番運用で利用する時にあらかじめレコードを登録している必要がありました。
そこで MySQL を直接叩いて dump するのもいいですが、 Nest.js の機能を利用して一括登録する方法がないか調べました。
方法
Nest.js ではサーバ起動時に特定の method を実行することができます。
そこで、データを登録する initializeData
method を実装し、 initializeData
method をサーバの起動時に実行するようにしました。
Service
例えば Product
という module を作成したいとします。
あらかじめ作成した JSON を読み込み Product object としてデータベースに登録する initializeData
method を以下のように product.service.ts
に追加します。
import { Injectable, NotFoundException } from '@nestjs/common'
// ...skipping
@Injectable()
export default class ProductService {
constructor(
@InjectModel('Product')
private readonly model: Model<Product, ProductKey>,
) {}
// ...skipping
+ async initializeData(): Promise<void> {
+ if ((await this.products()).length > 0) {
+ return
+ }
+
+ const product = ProductJson as any
+ await this.create(product)
+ }
}
initializeData
method はサーバが起動するたびに実行されるようにしたいのですが、開発中は何度もサーバを再起動することがあり、その度に同じレコードを登録しようとすると Product テーブルに適応している Unique 制約でレコードが登録できずエラーを吐いて強制終了してしまいます。
そこで、 (await this.products()).length > 0
として、すでにレコードが登録されている場合は何もしないようにしました。
Module
続けて Product.module.ts
をアップデートします。
- import { Module } from '@nestjs/common'
+ import { Logger, Module, OnModuleInit } from '@nestjs/common'
+ import { ConfigModule, ConfigService } from '@nestjs/config'
import SurveyItemController from './survey-item.controller'
import SurveyItemService from './survey-item.service'
+ const logger = new Logger('ProductModule')
@Module({
- imports: [],
+ imports: [ConfigModule.forRoot()],
controllers: [ProductController],
providers: [ProductService],
exports: [ProductService],
})
- export default class ProductModule {}
+ export default class ProductModule implements OnModuleInit {
+ constructor(
+ private readonly productService: ProductService,
+ private readonly configService: ConfigService,
+ ) {}
+
+ async onModuleInit() {
+ const stage = this.configService.get<string>('STAGE')
+ if (stage === 'test') {
+ return
+ }
+ await this.productService.initializeData()
+ logger.log("Product initialized")
+ }
+ }
ProductModule
を OnModuleInit
を継承するようにアップデートします。 続けて onModuleInit
method を実装します onModuleInit
method はサーバ起動時に必ず一度実行される method です。
この method で先ほど実装した initializeData
method を実行します。
また、自動でレコードが登録される機能は今回はユニットテスト時には不要でしたので .env
の STAGE
を参照し test
の場合はスキップするようにしました。
STAGE="test"
実行
実行されるかサーバを起動して確認します。
npm run start:dev
[7:38:37 AM] Starting compilation in watch mode...
[7:39:29 AM] Found 0 errors. Watching for file changes.
[Nest] 70254 - 02/01/2024, 7:40:26 AM LOG [NestFactory] Starting Nest application...
[Nest] 70254 - 02/01/2024, 7:40:26 AM LOG [InstanceLoader] ConfigHostModule dependencies initialized +0ms
[Nest] 70254 - 02/01/2024, 7:40:26 AM LOG [InstanceLoader] AppModule dependencies initialized +0ms
[Nest] 70254 - 02/01/2024, 7:40:26 AM LOG [InstanceLoader] ConfigModule dependencies initialized +1ms
[Nest] 70254 - 02/01/2024, 7:40:26 AM LOG [InstanceLoader] ConfigModule dependencies initialized +0ms
...skipping
[Nest] 70254 - 02/01/2024, 7:40:27 AM LOG [NestApplication] Nest application successfully started +148ms
[Nest] 70254 - 02/01/2024, 7:40:27 AM LOG [ProductModule] Product initialized
ログの末尾を見ると ProductModule
の onModuleInit
method が実行されているのがわかります。
💡 まとめ
- サーバ利用時に必要なレコードを一括で登録できるようにしました
-
onModuleInit
method を利用することでサーバ起動時にさせたい作業を決めることができます -
test
時には実行しないなど環境で切り分けることもできました
参考
Discussion