I am currently reading the documentation for a Nest.js application using Prisma. Following the documentation, I have created the following Service.
import { INestApplication, OnModuleInit } from "@nestjs/mon";
import { PrismaClient } from "@prisma/client";
export class PrismaService extends PrismaClient implements OnModuleInit {
async onModuleInit() {
console.log('onModuleInit PrismaService')
await this.$connect();
}
async enableShutdownHooks(app: INestApplication){
this.$on('beforeExit', async () => {
console.log('PrismaService enableShutdownHooks beforeExit')
await app.close();
});
}
}
The documentation described the use of the following.
import { Module } from '@nestjs/mon';
import { Prisma } from '@prisma/client';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { CsvParsersModule } from './csv-parsers/csv-parsers.module';
import { PrismaModule } from './prisma/prisma.module';
import { PrismaService } from './prisma/prisma.service';
@Module({
imports: [CsvParsersModule],
controllers: [AppController],
providers: [AppService, PrismaService]
})
export class AppModule {}
However, such code will cause onModuleInit to be called repeatedly when the Service is registered elsewhere.
import { Global, Module } from '@nestjs/mon';
import { CsvParsersService } from './csv-parsers.service';
import { CsvParsersController } from './csv-parsers.controller';
import { PrismaService } from 'src/prisma/prisma.service';
@Module({
controllers: [CsvParsersController],
providers: [CsvParsersService, PrismaService],
exports: [CsvParsersService]
})
export class CsvParsersModule {}
[Nest] 78737 - 2022/07/21 8:05:23 LOG [NestFactory] Starting Nest application...
[Nest] 78737 - 2022/07/21 8:05:23 LOG [InstanceLoader] AppModule dependencies initialized +25ms
[Nest] 78737 - 2022/07/21 8:05:23 LOG [InstanceLoader] CsvParsersModule dependencies initialized +0ms
[Nest] 78737 - 2022/07/21 8:05:23 LOG [RoutesResolver] AppController {/}: +8ms
[Nest] 78737 - 2022/07/21 8:05:23 LOG [RouterExplorer] Mapped {/, GET} route +1ms
[Nest] 78737 - 2022/07/21 8:05:23 LOG [RoutesResolver] CsvParsersController {/csv-parsers}: +0ms
[Nest] 78737 - 2022/07/21 8:05:23 LOG [RouterExplorer] Mapped {/csv-parsers, POST} route +0ms
onModuleInit PrismaService
onModuleInit PrismaService
[Nest] 78737 - 2022/07/21 8:05:23 LOG [NestApplication] Nest application successfully started +51ms
I think the best process is for onModuleInit to be called only once. How should this be resolved?
I am currently reading the documentation for a Nest.js application using Prisma. Following the documentation, I have created the following Service.
import { INestApplication, OnModuleInit } from "@nestjs/mon";
import { PrismaClient } from "@prisma/client";
export class PrismaService extends PrismaClient implements OnModuleInit {
async onModuleInit() {
console.log('onModuleInit PrismaService')
await this.$connect();
}
async enableShutdownHooks(app: INestApplication){
this.$on('beforeExit', async () => {
console.log('PrismaService enableShutdownHooks beforeExit')
await app.close();
});
}
}
The documentation described the use of the following.
import { Module } from '@nestjs/mon';
import { Prisma } from '@prisma/client';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { CsvParsersModule } from './csv-parsers/csv-parsers.module';
import { PrismaModule } from './prisma/prisma.module';
import { PrismaService } from './prisma/prisma.service';
@Module({
imports: [CsvParsersModule],
controllers: [AppController],
providers: [AppService, PrismaService]
})
export class AppModule {}
However, such code will cause onModuleInit to be called repeatedly when the Service is registered elsewhere.
import { Global, Module } from '@nestjs/mon';
import { CsvParsersService } from './csv-parsers.service';
import { CsvParsersController } from './csv-parsers.controller';
import { PrismaService } from 'src/prisma/prisma.service';
@Module({
controllers: [CsvParsersController],
providers: [CsvParsersService, PrismaService],
exports: [CsvParsersService]
})
export class CsvParsersModule {}
[Nest] 78737 - 2022/07/21 8:05:23 LOG [NestFactory] Starting Nest application...
[Nest] 78737 - 2022/07/21 8:05:23 LOG [InstanceLoader] AppModule dependencies initialized +25ms
[Nest] 78737 - 2022/07/21 8:05:23 LOG [InstanceLoader] CsvParsersModule dependencies initialized +0ms
[Nest] 78737 - 2022/07/21 8:05:23 LOG [RoutesResolver] AppController {/}: +8ms
[Nest] 78737 - 2022/07/21 8:05:23 LOG [RouterExplorer] Mapped {/, GET} route +1ms
[Nest] 78737 - 2022/07/21 8:05:23 LOG [RoutesResolver] CsvParsersController {/csv-parsers}: +0ms
[Nest] 78737 - 2022/07/21 8:05:23 LOG [RouterExplorer] Mapped {/csv-parsers, POST} route +0ms
onModuleInit PrismaService
onModuleInit PrismaService
[Nest] 78737 - 2022/07/21 8:05:23 LOG [NestApplication] Nest application successfully started +51ms
I think the best process is for onModuleInit to be called only once. How should this be resolved?
Share Improve this question edited Jul 20, 2022 at 23:14 marsquai asked Jul 20, 2022 at 23:12 marsquaimarsquai 311 silver badge3 bronze badges 04 Answers
Reset to default 4First, if you don't have, you should create a folder "prisma" or whatever and inside this folder you should insert your prisma.service.ts
import { INestApplication, OnModuleInit } from "@nestjs/mon";
import { PrismaClient } from "@prisma/client";
export class PrismaService extends PrismaClient implements OnModuleInit {
async onModuleInit() {
console.log('onModuleInit PrismaService') // you can keep this console.log only to confirm that's right
await this.$connect();
}
async enableShutdownHooks(app: INestApplication){
this.$on('beforeExit', async () => {
console.log('PrismaService enableShutdownHooks beforeExit')
await app.close();
});
}
}
After you should create a file prisma.module.ts and insert:
import { Global, Module } from '@nestjs/mon';
import { PrismaService } from './prisma.service';
@Global()
@Module({
providers: [PrismaService],
exports: [PrismaService],
})
export class PrismaModule {}
Now, in all modules that you need PrismaService you should remove PrismaService that is in Providers and instead add PrismaModule as import, that way:
import { Global, Module } from '@nestjs/mon';
import { CsvParsersService } from './csv-parsers.service';
import { CsvParsersController } from './csv-parsers.controller';
import { PrismaModule } from 'src/prisma/prisma.module';
@Module({
imports: [PrismaModule],
controllers: [CsvParsersController],
providers: [CsvParsersService],
exports: [CsvParsersService]
})
export class CsvParsersModule {}
I had this problem and nothing was working, the key the answer is the @Global() inside module Prisma, create a module for the Prisma and import PrismaModule inside another modules.
you registered that service twice, that's why you got two instances of the same class.
You can just add it once in some of those modules, then export it to the another one.
The problem is that you import the Prisma service two times at AppModule and CsvParsersModule. When declaring Prisma service as a provider in other modules Nest creates a new instance for each provider.
I guess it better have a dedicated module for Prisma, where you have to export Prisma service. Then you only need to import the module Prisma in the module you need to use Prisma Service to have only one instance of Prisma.
When you import a module in other module, you can use the export instances from the imported module. you can use the exported service whitout include in providers at new module. In this way you have only one instances of service. If you include in providers you creates a new instance.
You can use global module for this purpose. In order to avoid import the module frecuently
I had exactly the same issue you had and the reason was that all the resources I found proposed to directly pass the PrismaService
as a Provider
itself to every module that requires it.
The problem with this approach is that each module will create a new instance of PrismaService
which is not ideal and will reveal such a problem when someone wants to do anything, especially when it es to lifecycle events being emitted (like the one you experienced with onModuleInit
).
What you can do is create a module that will export the PrismaService (e.g PrismaModule), which is designed to be a Singleton As per Nest.js Docs
I quote from the docs
In Nest, modules are singletons by default, and thus you can share the same instance of any provider between multiple modules effortlessly.
So, first, let's start by creating a module prisma.module.ts
which will serve this purpose.
import { Module } from '@nestjs/mon';
import { PrismaService } from './prisma.service';
@Module({
providers: [PrismaService],
exports: [PrismaService],
})
export class PrismaModule {}
From there, there are two options.
Option 1 - Inject the module globally
At this point, we need to define that module as Global
by providing add the necessary decorator to the PrismaModule
by modifying the prisma.module.ts
like shown below:
import { Global, Module } from '@nestjs/mon';
import { PrismaService } from './prisma.service';
@Global()
@Module({
providers: [PrismaService],
exports: [PrismaService],
})
export class PrismaModule {}
Finally, we have to inject this module in the app.module.ts
@Module({
imports: [
// various imports
PrismaModule, // this is the important change
],
controllers: [
// our controllers
],
providers: [
// our providers
],
})
export class AppModule {}
Now, we can directly use the PrismaService
in every module of the app.
Option 2 - Import the PrismaModule to each module seperately
Import the PrismaModule
in each module we wish to use it at.
Let's say we have a module named RandomModule
. What we have to do is to import the PrismaModule
to the random.module.ts
file as shown below:
import { Module } from '@nestjs/mon';
import { RandomService } from './cities.service';
import { RandomController } from './cities.controller';
import { PrismaModule } from './prisma.module.ts'
@Module({
imports: [PrismaModule], // this is the important bit
controllers: [RandomController],
providers: [RandomService],
})
export class RandomModule {}
I hope the explanation was helpful. Please, in case something was unclear, feel free to ask right away.