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.
- Module with PrismaService
- Module with RedisConnection(is Injectable)
- 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.
- Module with PrismaService
- Module with RedisConnection(is Injectable)
- 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 badge1 Answer
Reset to default 0Since this module is not marked as
@Global()
, other modules that needPrismaService
orRedisConnection
must explicitly importConnectionModule.
The
register
method creates a dynamic module, but this approach prevents dependency injection becausePrismaService
andRedisConnection
might require configurations.Directly pushing classes like
PrismaService
andRedisConnection
into theproviders
array means NestJS won't have full control over dependency injection.It’s better to use
{ provide, useClass }
or{ provide, useFactory }
when handling services dynamically.f multiple modules register
ConnectionModule
with different configurations, it could lead to multiple instances ofPrismaService
orRedisConnection
, breaking NestJS's singleton pattern.Instead, use a
useFactory
function withinject
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, }; } }