You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在NestJS中共享动态模块?自定义模块遇实例重复问题

NestJS 动态模块配置疑问:EmailModule为何无法像ConfigModule那样全局复用?

问题场景

我写了一个动态模块EmailModule:

@Injectable()
export class EmailSender {}

@Module({})
export class EmailModule {
  static forRoot(options: { smtpHost: string; smtpPassword: string }): DynamicModule {
    return {
      module: EmailModule,
      providers: [EmailSender,],
      exports: [EmailSender,],
    };
  }
}

同时有包含CatModule的MainModule:

@Injectable()
export class CatService {
  constructor(emailSender: EmailSender) {}
}

@Module({
  imports: [
    EmailModule,
  ],
  providers: [CatService,],
  exports: [CatService,],
})
export class CatModule {}

@Module({
  imports: [
    EmailModule.forRoot({
      smtpHost: 'localhost',
      smtpPassword: 'password',
    }),
    CatModule,
  ],
})
export class MainModule {}

这段代码跑不起来,CatService无法解析EmailSender。我必须在CatModule里导入EmailModule.forRoot({host, password})才能用,但我不想这么做——毕竟还有DogModule、HorseModule等一堆子模块,总不能每个都重复写.forRoot({...})。我希望只在主模块调用一次.forRoot({...})完成配置,所有子模块直接导入EmailModule就能用。

我确定这是可行的,因为NestJS自带的ConfigModule就是这么玩的,换成它之后一切正常:

@Injectable()
export class CatService {
  constructor(configService: ConfigService) {}
}

@Module({
  imports: [
    ConfigModule,
  ],
  providers: [CatService,],
  exports: [CatService,],
})
export class CatModule {}

@Module({
  imports: [
    ConfigModule.forRoot({
      envFilePath: '../.env',
    }),
    CatModule,
  ],
})
export class MainModule {}

搞不懂为什么ConfigModule能正常工作,我的EmailModule却不行?它到底有什么特殊之处?


更新:尝试解决方案但无效

我按照方案改了代码,但还是不行:

import {type DynamicModule, Inject, Injectable, Module,} from '@nestjs/common';
import {NestFactory,} from '@nestjs/core';


interface EmailModuleOptions {
  smtpHost: string;
  smtpPassword: string;
}
const EMAIL_MODULE_OPTIONS = 'EMAIL_MODULE_OPTIONS';

@Injectable()
export class EmailSender {
  constructor(
    @Inject(EMAIL_MODULE_OPTIONS)
    private readonly config: EmailModuleOptions) {
    console.log('EmailSender constructor, ', config);
  }

  sendEmail(): void {
    console.log({
      message: 'Meow',
      host: this.config.smtpHost,
      password: this.config.smtpPassword,
    });
  }
}

@Module({ })
export class EmailModule {

  private static emailOptions = {};

  static forRoot(options: EmailModuleOptions): DynamicModule {
    this.emailOptions = options;

    return EmailModule.forFeature();
  }

  static forFeature(): DynamicModule {
    return {
      module: EmailModule,
      providers: [
        EmailSender,
        {
          provide: EMAIL_MODULE_OPTIONS,
          useValue: EmailModule.emailOptions,
        },
      ],
      exports: [EmailSender,],
    };
  }
}

@Injectable()
export class CatService {
  constructor(private readonly emailSender: EmailSender) {}

  sayHello(): void {
    this.emailSender.sendEmail();
  }
}

@Module({
  imports: [
    EmailModule.forFeature(),
  ],
  providers: [CatService,],
  exports: [CatService,],
})
export class CatModule {}

@Module({
  imports: [
    EmailModule.forRoot({
      smtpHost: 'localhost',
      smtpPassword: 'password',
    }),
    CatModule,
  ],
})
export class MainModule {}

const main = async (): Promise<void> => {
  const app = await NestFactory.createApplicationContext(MainModule);
  const catService = app.get(CatService);
  catService.sayHello();
};
main();

控制台输出如下:

[Nest] 30872  - 04/06/2023, 2:46:37 PM     LOG [NestFactory] Starting Nest application...
EmailSender constructor,  { smtpHost: 'localhost', smtpPassword: 'password' }
EmailSender constructor,  {}
[Nest] 30872  - 04/06/2023, 2:46:37 PM     LOG [InstanceLoader] MainModule dependencies initialized +29ms
[Nest] 30872  - 04/06/2023, 2:46:37 PM     LOG [InstanceLoader] EmailModule dependencies initialized +1ms
[Nest] 30872  - 04/06/2023, 2:46:37 PM     LOG [InstanceLoader] EmailModule dependencies initialized +0ms
[Nest] 30872  - 04/06/2023, 2:46:37 PM     LOG [InstanceLoader] CatModule dependencies initialized +0ms
{ message: 'Meow', host: undefined, password: undefined }

明显NestJS创建了两个EmailSender实例,而CatService用的是那个没有配置的空实例。


内容的提问来源于stack exchange,提问作者Farad

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.28 12:54:57