Reputation: 1130
I have got a NestJs app, that uses two services. The DbService that connects to the Db and the SlowService that does stuff rather slow and uses the injected DbService.
Now the app shall provide health routes outside of the api base path, so i need a different module that provides the controllers for the health routes.
I created a base module.
import { Module } from '@nestjs/common'
import { SlowService } from './slow.service'
import { DbService } from './db.service'
@Module({
imports: [],
controllers: [],
providers: [DbService, SlowService],
exports: [DbService, SlowService]
})
export class BaseModule {
}
The ApiModule and the HealthModule now both import the base module to be able to use the services.
imports: [BaseModule],
There is only a small problem. Both modules seem to construct their own instance of the service but I need it to be the same instance. I assume this, because the console.log from the constructor appear twice when starting the app. Am I missing a setting or something?
UPDATE
Here is my bootstrap method, so you can see how I initialize the modules.
async function bootstrap (): Promise<void> {
const server = express()
const api = await NestFactory.create(AppModule, server.application, { cors: true })
api.setGlobalPrefix('api/v1')
await api.init()
const options = new DocumentBuilder()
.setTitle('...')
.setLicense('MIT', 'https://opensource.org/licenses/MIT')
.build()
const document = SwaggerModule.createDocument(api, options)
server.use('/swaggerui', SwaggerUI.serve, SwaggerUI.setup(document))
server.use('/swagger', (req: express.Request, res: express.Response, next?: express.NextFunction) => res.send(document))
const health = await NestFactory.create(HealthModule, server.application, { cors: true })
health.setGlobalPrefix('health')
await health.init()
http.createServer(server).listen(Number.parseInt(process.env.PORT || '8080', 10))
}
const p = bootstrap()
Upvotes: 14
Views: 22070
Reputation: 1002
Maybe you defined the services as providers for 2 modules. What you need to do is only define your BaseModule
as import in the module where you need it.
This example demonstrates the service OtherService
in OtherModule
which needs the DbService
from BaseModule
. If you run the example you will see that it only instantiates the DbService
once.
import {Injectable, Module} from '@nestjs/common';
import {NestFactory} from '@nestjs/core';
@Injectable()
export class SlowService {
constructor() {
console.log(`Created SlowService`);
}
}
@Injectable()
export class DbService {
constructor() {
console.log(`Created DbService`);
}
}
@Module({
imports: [],
providers: [SlowService, DbService],
exports: [SlowService, DbService]
})
export class BaseModule {}
@Injectable()
export class OtherService {
constructor(private service: DbService) {
console.log(`Created OtherService with dependency DbService`);
}
}
@Module({
imports: [BaseModule],
providers: [OtherService],
})
export class OtherModule {}
@Module({
imports: [
BaseModule,
OtherModule
],
})
export class AppModule {}
NestFactory.createApplicationContext(AppModule).then((app) => console.log('🥑 context created'));
This gist demonstrates BAD usage of providers, resulting in instantiating the DbService
twice: https://gist.github.com/martijnvdbrug/12faf0fe0e1fc512c2a73fba9f31ca53
Upvotes: 26
Reputation: 477
I just drop it here in case someone else needed this.
If you really want every modules in your app sharing the same instance, then probably you need to use global variable and put your services inside of it.
First, define your service on the root of your source app, which is app.module
.
nest g s shared-services/db
Second, mention your service in the global variable. (Focus on commented code)
import { Injectable } from '@nestjs/common';
/* Define global variable as "any" so we don't get nasty error. */
declare var global: any;
@Injectable()
export class DbService {
constructor() {
console.log(`Created DbService`);
/* Put the class inside global variable. */
global.dbService = this;
}
}
Finally, you can call your service from other controller, or services.
import { Injectable } from '@nestjs/common';
import { DbService} from './../shared-services/db.service';
/* Define global variable as "any" so we don't get nasty error. */
declare var global: any;
@Injectable()
export class OtherService {
/* Call the service. */
protected readonly dbService: DbService = global.dbService;
constructor() {
}
}
And you are good to go. I really hope in the future NestJs has the same Injectable features as Angular did so we don't really need to bothered about export-import any services.
Upvotes: -3