動的モジュールは、インポート時に設定可能なモジュールです — 静的な @Module({...}) の代わりに、慣例的に forRoot と forFeature という静的メソッドを公開し、渡された引数で設定されたモジュールを返します。これが、設定可能で再利用可能なモジュール(設定、データベース、JWT)がオプションを受け入れる方法です。
A static module is fixed — but reusable modules (DatabaseModule, ConfigModule,
JwtModule) need to be CONFIGURED per app (connection URL, secret, options).
Dynamic modules let a module accept configuration at import time.
@Module({
imports: [
ConfigModule.forRoot({ isGlobal: true }), // ← forRoot with options
TypeOrmModule.forRoot({ type: "postgres", url: ... }), // ← configured at import
JwtModule.register({ secret: process.env.SECRET }), // ← register variant
TypeOrmModule.forFeature([User, Post]), // ← forFeature for specifics
],
})
export class AppModule {}
モジュールをインポートする際に見かける .forRoot()/.register()/.forFeature() の呼び出しは、設定を受け入れる動的モジュールのメソッドです。
forRoot() → configure the module ONCE, globally (the connection, the secret, core setup)
called in the root/app module a single time
forFeature() → register feature-specific pieces in each feature module that needs them
(e.g. TypeOrmModule.forFeature([User]) registers entities per module)
慣例としては、forRoot はグローバル/1回限りの設定用で、forFeature は複数のモジュールで呼び出す機能ごとの登録用です。
@Module({})
export class StorageModule {
static forRoot(options: StorageOptions): DynamicModule {
return { // returns a configured module
module: StorageModule,
providers: [
{ provide: "STORAGE_OPTIONS", useValue: options }, // inject the passed options
StorageService,
],
exports: [StorageService],
global: true, // optionally make it global
};
}
}
// usage: StorageModule.forRoot({ bucket: "my-bucket", region: "us-east-1" })
静的メソッドは DynamicModule オブジェクト(モジュール + プロバイダー + エクスポート)を返し、渡されたオプションをモジュールのプロバイダーに配線します(多くの場合、インジェクショントークン経由)。
// configure using OTHER injected dependencies (e.g. ConfigService) — async/factory
StorageModule.forRootAsync({
useFactory: (config: ConfigService) => ({ bucket: config.get("BUCKET") }),
inject: [ConfigService],
});
forRootAsync バリアントは、設定が他のプロバイダー(ConfigService など)に依存することを可能にします — オプションが検証された設定から来る場合に不可欠です。
動的モジュールと forRoot/forFeature パターンは、設定可能で再利用可能なモジュールを構築するための重要な NestJS コンセプトです — そしてそれらを理解することは、エコシステムを使用する場合と共有モジュールを構築する場合の両方で価値があります。
ほぼすべての主要な NestJS モジュール(ConfigModule、TypeOrmModule、JwtModule、BullModule、CacheModule)は、.forRoot()/.register()/.forFeature() で設定される動的モジュールです。そのため、これらの呼び出しが何であるか(静的インポートではなく、インポート時にモジュールに設定を渡す)を認識することは、エコシステム全体がどのように配線されているかを明確にします。
forRoot vs forFeature 慣例(データベース接続やシークレットなどの1回限りのグローバルセットアップに forRoot を使用し、各モジュールのエンティティ登録などの機能ごとの登録に forFeature を使用)は、常に遭遇するパターンです。
シニア開発者にとって、独自の動的モジュールを構築する方法(渡されたオプションから設定されたプロバイダーを備えた DynamicModule を返す、多くの場合インジェクショントークン経由、設定依存のセットアップに非同期バリアントを使用)を知ることは、再利用可能で設定可能なモジュール — 共有インフラストラクチャ(ストレージ、外部 API クライアント、カスタム設定)を作成することができます。アプリのその他の部分や他のプロジェクトが独自の設定でインポートします。
これはより高度なトピックですが、NestJS のモジュラーアーキテクチャを深く理解し、プロフェッショナルで再利用可能なモジュールライブラリを構築するために不可欠です。これにより、大規模でよく構造化された NestJS システムを設計するための重要な能力となります。