最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

nestjs - Nest.js Dynamic Modules and Injectable Singleton - Stack Overflow

programmeradmin5浏览0评论

Introduction

Have a dynamic module, which returns module with connection providers.

@Module({})
export class ConnectionModule {
  static register(cnf: ConnectionsList) {
    const cons = [];
    if (cnf.db) cons.push(PrismaService);
    if (cnf.keyValueStorage) cons.push(RedisConnection);

    return {
      module: ConnectionModule,
      exports: cons,
      providers: cons,
    };
  }
}

There's 3 possible combination.

  1. Module with PrismaService
  2. Module with RedisConnection(is Injectable)
  3. Module with both services

Problem

PrismaService and RedisConnection recreating 2 times

When I use only first and second combination its works as should, but after that when I add module which use both Services I'm getting next:

Logs:

2025-03-18T21:28:51.686Z [RedisConnection] info: Redis connected

2025-03-18T21:28:51.694Z [PrismaService] info: Prisma Client connected to Database

2025-03-18T21:28:51.703Z [PrismaService] info: Prisma Client connected to Database

2025-03-18T21:28:51.705Z [RedisConnection] info: Redis connected


I'm assuming that Nest.js creates 3 different singleton modules from ConnectionModule, but recreating Services in each of these modules. As I understand, Injectable Service is singleton by default according to documetation

ReddisConnection

@Injectable()
export class RedisConnection implements OnModuleInit {
  private keyValueStorage: Redis;

  constructor(
    private readonly configService: ConfigService,
  ) {}

  async onModuleInit() {
    this.keyValueStorage = new Redis(
      this.configService.get<string>('REDIS_URL'),
    );
  }

  public getKeyValueStorage() {
    return this.keyValueStorage;
  }
}

*Logging in this.keyValueStorage.on('connect')

PrismaService

@Injectable()
export class PrismaService extends PrismaClient implements OnModuleInit {
  async onModuleInit() {
    await this.$connect();
  }
}

*Logging after this.$connect();

Am I missing something or its a Nest.js bug?

Introduction

Have a dynamic module, which returns module with connection providers.

@Module({})
export class ConnectionModule {
  static register(cnf: ConnectionsList) {
    const cons = [];
    if (cnf.db) cons.push(PrismaService);
    if (cnf.keyValueStorage) cons.push(RedisConnection);

    return {
      module: ConnectionModule,
      exports: cons,
      providers: cons,
    };
  }
}

There's 3 possible combination.

  1. Module with PrismaService
  2. Module with RedisConnection(is Injectable)
  3. Module with both services

Problem

PrismaService and RedisConnection recreating 2 times

When I use only first and second combination its works as should, but after that when I add module which use both Services I'm getting next:

Logs:

2025-03-18T21:28:51.686Z [RedisConnection] info: Redis connected

2025-03-18T21:28:51.694Z [PrismaService] info: Prisma Client connected to Database

2025-03-18T21:28:51.703Z [PrismaService] info: Prisma Client connected to Database

2025-03-18T21:28:51.705Z [RedisConnection] info: Redis connected


I'm assuming that Nest.js creates 3 different singleton modules from ConnectionModule, but recreating Services in each of these modules. As I understand, Injectable Service is singleton by default according to documetation https://docs.nestjs/fundamentals/injection-scopes

ReddisConnection

@Injectable()
export class RedisConnection implements OnModuleInit {
  private keyValueStorage: Redis;

  constructor(
    private readonly configService: ConfigService,
  ) {}

  async onModuleInit() {
    this.keyValueStorage = new Redis(
      this.configService.get<string>('REDIS_URL'),
    );
  }

  public getKeyValueStorage() {
    return this.keyValueStorage;
  }
}

*Logging in this.keyValueStorage.on('connect')

PrismaService

@Injectable()
export class PrismaService extends PrismaClient implements OnModuleInit {
  async onModuleInit() {
    await this.$connect();
  }
}

*Logging after this.$connect();

Am I missing something or its a Nest.js bug?

Share Improve this question edited Mar 18 at 22:06 Даниил Зеленко asked Mar 18 at 21:56 Даниил ЗеленкоДаниил Зеленко 11 bronze badge
Add a comment  | 

1 Answer 1

Reset to default 0
  1. Since this module is not marked as @Global(), other modules that need PrismaService or RedisConnection must explicitly import ConnectionModule.

  2. The register method creates a dynamic module, but this approach prevents dependency injection because PrismaService and RedisConnection might require configurations.

  3. Directly pushing classes like PrismaService and RedisConnection into the providers array means NestJS won't have full control over dependency injection.

  4. It’s better to use { provide, useClass } or { provide, useFactory } when handling services dynamically.

  5. f multiple modules register ConnectionModule with different configurations, it could lead to multiple instances of PrismaService or RedisConnection, breaking NestJS's singleton pattern.

  6. Instead, use a useFactory function with inject to ensure dependencies are handled properly.

    Try something like:

    @Global()
    @Module()
    export class ConnectionModule {
      static register(cnf: ConnectionsList): DynamicModule {
        const providers = [];
    
        if (cnf.db) {
          providers.push({
            provide: PrismaService,
            useFactory: () => new PrismaService(/* pass config if needed */),
          });
        }
    
        if (cnf.keyValueStorage) {
          providers.push({
            provide: RedisConnection,
            useFactory: () => new RedisConnection(/* pass config if needed */),
          });
        }
    
        return {
          module: ConnectionModule,
          providers,
          exports: providers,
        };
      }
    }
    
发布评论

评论列表(0)

  1. 暂无评论