← 解説「動的モジュール — forRoot パターン」に戻る
サンプルコードで身につける: 動的モジュール — forRoot パターン
解説で学んだ概念を、5つの具体例で確認します。素朴な例から実務寄りの例へと進みます。
1静的メソッドが DynamicModule を返す
動的モジュールの骨格は、forRoot のような静的メソッドが { module, providers, exports } という DynamicModule オブジェクトを返すことです。@Module({}) の中身を、メソッドの中で実行時に組み立てて返すのが通常モジュールとの違いです。
import { Module, DynamicModule } from "@nestjs/common";
@Module({})
export class GreetingModule {
// 静的メソッドが構成を組み立てて返す
static forRoot(): DynamicModule {
return {
module: GreetingModule,
providers: [GreetingService],
exports: [GreetingService],
};
}
}
// 利用側は @Module の imports でメソッドを呼ぶ
// imports: [GreetingModule.forRoot()]
2options を useValue でトークンに載せる
forRoot が受け取った設定を、useValue でトークンに載せてモジュール内のサービスに注入可能にする定番の流れです。呼び出し側から渡した bucket が、サービスから @Inject 経由で読めるようになります。
import { Module, DynamicModule, Injectable, Inject } from "@nestjs/common";
export type StorageOptions = { bucket: string };
@Injectable()
export class StorageService {
constructor(@Inject("STORAGE_OPTIONS") private opts: StorageOptions) {}
bucketName() {
return this.opts.bucket;
}
}
@Module({})
export class StorageModule {
static forRoot(options: StorageOptions): DynamicModule {
return {
module: StorageModule,
// 渡された設定をトークンに載せて注入可能にする
providers: [
{ provide: "STORAGE_OPTIONS", useValue: options },
StorageService,
],
exports: [StorageService],
};
}
}
3forRoot パターンの本質をプレーンTSで再現
「設定を受け取り、その場で providers を組み立て、トークンに載せた値をサービスへ注入する」という forRoot の核心を、デコレータなしの関数で再現した実行例です。動的モジュールが実行時に構成を生成しているだけだと分かります。
TypeScript
4forRoot と forFeature の使い分け
全体設定は forRoot で一度だけ、機能ごとに繰り返す登録は forFeature で、という慣習を示します。forFeature は全体設定を再受け取りせず、対象だけを受け取って軽量な構成を返すのが定石です。
import { Module, DynamicModule } from "@nestjs/common";
@Module({})
export class OrmModule {
// 全体設定: AppModule で一度だけ。接続などを構築する
static forRoot(options: { url: string }): DynamicModule {
return {
module: OrmModule,
global: true, // 全体で使えるようにする
providers: [{ provide: "ORM_CONNECTION", useValue: options }],
exports: ["ORM_CONNECTION"],
};
}
// 機能ごと: 各機能モジュールで繰り返す。設定は再受け取りしない
static forFeature(entities: string[]): DynamicModule {
return {
module: OrmModule,
providers: [{ provide: "ORM_ENTITIES", useValue: entities }],
exports: ["ORM_ENTITIES"],
};
}
}
// AppModule: imports: [OrmModule.forRoot({ url })]
// UsersModule: imports: [OrmModule.forFeature(["User"])]
5実務: ConfigModule.forRoot 風の動的モジュール
ConfigModule.forRoot({ isGlobal, envFilePath }) を自作するイメージの実務例です。読み込んだ設定を ConfigService に載せ、global: true でアプリ全体から再 import なしに使えるようにする、というよく見る形を再現します。
import { Module, DynamicModule, Injectable, Inject } from "@nestjs/common";
export type ConfigOptions = { isGlobal?: boolean; values: Record<string, string> };
@Injectable()
export class ConfigService {
constructor(@Inject("CONFIG_VALUES") private values: Record<string, string>) {}
get(key: string): string | undefined {
return this.values[key];
}
}
@Module({})
export class ConfigModule {
static forRoot(options: ConfigOptions): DynamicModule {
return {
module: ConfigModule,
global: options.isGlobal, // isGlobal: true で全体から使える
providers: [
{ provide: "CONFIG_VALUES", useValue: options.values },
ConfigService,
],
exports: [ConfigService],
};
}
}
// imports: [ConfigModule.forRoot({ isGlobal: true, values: process.env })]