nestjs use service from external module inside a dynamic module - nestjs

AppModule
#Module({
imports: [
ConfigModule.forRoot({
isGlobal: true,
envFilePath: !!ENV ? `.env.${ENV}` : '.env',
}),
AedesModule.forRootAsync({
useFactory: (configService: ConfigService) => ({
port: 1883,
}),
inject: [ConfigService],
}),
AuthModule,
],
controllers: [],
providers: [],
})
export class AppModule {}
My AEDES MODULE:
import { DynamicModule, Global, Module, Provider } from '#nestjs/common';
import { AedesModuleAsyncOptions, AedesModuleOptions } from './#types/package';
import { AedesService } from './aedes.service';
#Global()
#Module({
providers: [AedesService],
exports: [AedesService],
})
export class AedesModule {
public static forRootAsync(options: AedesModuleAsyncOptions): DynamicModule {
const customOptions = this.getCustomOptions(options);
return {
module: AedesModule,
imports: options.imports || [],
providers: [customOptions, this.aedesProvider],
exports: [customOptions, this.aedesProvider],
};
}
private static getCustomOptions(options: AedesModuleAsyncOptions): Provider {
return {
provide: 'AEDES_OPTIONS',
useFactory: options.useFactory,
inject: options.inject || [],
};
}
private static aedesProvider: Provider = {
provide: AedesService,
useFactory: async (options: AedesModuleOptions) => {
const aedes = new AedesService(options);
aedes.init();
return aedes;
},
inject: ['AEDES_OPTIONS'],
};
private static getOptionsProvider(options: AedesModuleOptions): Provider {
return {
provide: 'AEDES_OPTIONS',
useValue: options,
};
}
}
type definitions:
import { ModuleMetadata } from '#nestjs/common';
export interface AedesModuleAsyncOptions
extends Pick<ModuleMetadata, 'imports'> {
inject?: any[];
useFactory: (
...args: any[]
) => Promise<AedesModuleOptions> | AedesModuleOptions;
}
export interface AedesModuleOptions {
port: number;
}
My Service:
export class AedesService {
public broker: aedes;
private port: number;
constructor(options: AedesModuleOptions) {
this.port = options.port;
}
init() {
this.broker = new aedes({
authenticate: async (client, username, password, done) => {
console.log(username, password);
const decoded: any =
await this.authService.manualAccessTokenVerification(
password.toString(),
); // I can't inject or import the existing authService which importing from another module.
console.log(decoded);
return done(null, true);
},
});
const mqttServer = createServer(this.broker);
mqttServer.listen(this.port, () => {
Logger.log(`MQTT server listening on ${this.port}`);
});
}
}
I can't inject or import the existing authService which importing from another module. Is it possible?

Related

get mongoConnection on beforeAll and AfterAll

I want to get the MongoConnection before my tests and remove all collections after the test.
I create a file like test-setup.js:
const mongoose = require('mongoose')
mongoose.promise = global.Promise
async function removeAllCollections () {
const collections = Object.keys(mongoose.connection.collections)
for (const collectionName of collections) {
const collection = mongoose.connection.collections[collectionName]
await collection.deleteMany()
}
}
async function dropAllCollections () {
const collections = Object.keys(mongoose.connection.collections)
for (const collectionName of collections) {
const collection = mongoose.connection.collections[collectionName]
console.log(collectionName)
try {
await collection.drop()
} catch (error) {
// Sometimes this error happens, but you can safely ignore it
if (error.message === 'ns not found') return
// This error occurs when you use it.todo. You can
// safely ignore this error too
if (error.message.includes('a background operation is currently running')) return
console.log(error.message)
}
}
}
module.exports = {
setupDB (databaseName, runSaveMiddleware = false) {
// Connect to Mongoose
beforeAll(async () => {
const url = 'mongodb://127.0.0.1:27017/myDb'
let conn = await mongoose.connect(url, { useNewUrlParser: true })
console.log(conn.collections)
})
// Seeds database before each test
// beforeEach(async () => {
// await seedDatabase(runSaveMiddleware)
// })
// Cleans up database between each test
afterEach(async () => {
await removeAllCollections()
})
// Disconnect Mongoose
afterAll(async () => {
await dropAllCollections()
await mongoose.connection.close()
})
}
}
At the moment the seedDatabase is commented, but I'd like to create a database per each e2e files, but it's a second step.
So, I add this file to jest-e2e.json
{
"moduleFileExtensions": ["js", "json", "ts"],
"rootDir": ".",
"testEnvironment": "node",
"testRegex": ".e2e-spec.ts$",
"transform": {
"^.+\\.(t|j)s$": "ts-jest"
},
"setupFilesAfterEnv": ["<rootDir>/test-setup.js"]
}
And on my test:
import * as request from 'supertest';
import {
APP_URL,
TESTER_EMAIL,
TESTER_PASSWORD,
MAIL_HOST,
MAIL_PORT,
} from '../utils/constants';
describe('Auth user (e2e)', () => {
const app = APP_URL;
const mail = `http://${MAIL_HOST}:${MAIL_PORT}`;
const newUserFirstName = `Tester${Date.now()}`;
const newUsername = `E2E.${Date.now()}`;
const newUserEmail = `User.${Date.now()}#example.com`;
const newUserPassword = `secret`;
const { setupDB } = require('../test-setup.js')
setupDB('users', true)
it('Login: /api/v1/auth/email/login (POST)', () => {
return request(app)
.post('/api/v1/auth/email/login')
.send({ email: TESTER_EMAIL, password: TESTER_PASSWORD })
.expect(404)
.expect(({ body }) => {
expect(404);
expect(body.error).toBe('Not Found');
});
});
it('Register new user: /api/v1/auth/email/register (POST)', async () => {
return request(app)
.post('/api/v1/auth/email/register')
.send({
email: newUserEmail,
password: newUserPassword,
name: newUserFirstName,
username: newUsername,
})
.expect(201);
});
The test works, but still using the "main" database, the same database that I can find on app.module
MongooseModule.forRootAsync({
imports: [ConfigModule],
useFactory: (configService: ConfigService) => ({
uri: configService.get('database.url'),
}),
inject: [ConfigService],
}),
How can use the new database per each tests and delete the collections after the tests?
If I put console.log on my test-setup.js I can see the output.
UPDATE:
app.module.ts
import { Module } from '#nestjs/common';
import { ConfigModule, ConfigService } from '#nestjs/config';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { UsersModule } from './users/users.module';
import { MongooseModule } from '#nestjs/mongoose';
import { AuthModule } from './auth/auth.module';
import { MailModule } from './mail/mail.module';
import { I18nModule } from 'nestjs-i18n/dist/i18n.module';
import { I18nJsonParser } from 'nestjs-i18n/dist/parsers/i18n.json.parser';
import { HeaderResolver } from 'nestjs-i18n';
import { MailerModule } from '#nestjs-modules/mailer';
import { MailConfigService } from './mail/mail-config.service';
import databaseConfig from './config/database.config';
import authConfig from './config/auth.config';
import appConfig from './config/app.config';
import mailConfig from './config/mail.config';
import * as path from 'path';
import { ForgotModule } from './forgot/forgot.module';
#Module({
imports: [
ConfigModule.forRoot({
isGlobal: true,
envFilePath: ['.env', '.env.dev', '.env.stage', '.env.prod'], //if a variable is found in multiple files, the first one takes precedence.
load: [
databaseConfig,
authConfig,
appConfig,
mailConfig,
],
}),
MongooseModule.forRootAsync({
imports: [ConfigModule],
useFactory: (configService: ConfigService) => ({
uri: configService.get('database.url'),
// useNewUrlParser: true,
// useFindAndModify: false,
// useCreateIndex: true,
}),
inject: [ConfigService],
}),
MailerModule.forRootAsync({
useClass: MailConfigService,
}),
I18nModule.forRootAsync({
useFactory: (configService: ConfigService) => ({
fallbackLanguage: configService.get('app.fallbackLanguage'),
parserOptions: {
path: path.join(
configService.get('app.workingDirectory'),
'src',
'i18n',
'translations',
),
},
}),
parser: I18nJsonParser,
inject: [ConfigService],
resolvers: [new HeaderResolver(['x-custom-lang'])],
}),
AuthModule,
UsersModule,
ForgotModule,
MailModule,
],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}

Mock Injected Twilio Service in Unit Testing Nest.js

I have a problem with overriding provider/setup module testing in nest.js application for testing.
Module file: smsModule.ts:
import { TwilioService } from './twilio/twilio.service';
import { DynamicModule, Module } from '#nestjs/common';
import { TwilioConfig, SMS_TWILIO_CONFIG } from './twilio.config';
import { TwilioClientCustom } from './twilio/twilio-client-custom';
#Module({
imports: [],
providers: [TwilioService],
})
export class SmsModule {
static register(options: TwilioConfig): DynamicModule {
return {
module: SmsModule,
imports: [HttpModule],
providers: [
{
provide: SMS_TWILIO_CONFIG,
useValue: options,
},
TwilioService,
TwilioClientCustom,
],
exports: [TwilioService],
};
}
}
Twilio client, config files:
//client
import { TwilioConfig, SMS_TWILIO_CONFIG } from '../twilio.config';
import { Twilio } from 'twilio';
import { Inject, Injectable } from '#nestjs/common';
#Injectable()
export class TwilioClientCustom extends Twilio {
constructor(#Inject(SMS_TWILIO_CONFIG) twilioConfig: TwilioConfig) {
super(twilioConfig.accountSid, twilioConfig.authToken);
}
}
//config
import { IsString, IsNotEmpty, NotContains, IsOptional, IsArray } from 'class-validator';
const INFO = 'Must be ....';
export class TwilioConfig {
#IsString()
#IsNotEmpty()
#NotContains('OVERRIDE_WITH_', { message: INFO })
accountSid: string;
#IsString()
#IsNotEmpty()
authToken: string;
#IsArray()
#IsOptional()
#IsNotEmpty('OVERRIDE_WITH_', { message: INFO })
serviceSid: string;
}
export const SMS_TWILIO_CONFIG = 'smsTwilioConfig';
Twilio service file: twilio.service.tst:
import { HttpService } from '#nestjs/axios';
import { TwilioConfig, SMS_TWILIO_CONFIG } from '../twilio.config';
import { SendSmsTwilioService } from './../sendsms.service';
import { Inject, Injectable } from '#nestjs/common';
import { TwilioClientCustom } from './twilio-client-custom';
#Injectable()
export class TwilioService implements SendSmsTwilioService {
constructor(
#Inject(SMS_TWILIO_CONFIG) private readonly config: TwilioConfig,
private readonly client: TwilioClientCustom,
private readonly httpService: HttpService
) {}
async sendSMS(to: string, from: string, body: string): Promise<string> {
......
return this.client.messages
.create({
to, //Recipinet's number
from, //Twilio number
body, //Messages to Recipient
})
.then((message) => message.sid)
.catch(() => {
throw new Error('TWILIO accountSid or authToken not valid');
});
}
I would like to test my service:
test file:
import { Test, TestingModule } from '#nestjs/testing';
//import { TWILIO_CONFIG_SPEC } from './test.config';
import { TwilioClientCustom } from '../src/twilio/twilio-client-custom';
import { HttpService } from '#nestjs/axios';
import { TwilioConfig } from './../src/twilio.config';
import { TwilioService } from './../src/twilio/twilio.service';
import nock from 'nock';
describe('TwilioService', () => {
let service: TwilioService;
let client: TwilioClientCustom;
let httpService: HttpService;
afterEach(() => {
nock.cleanAll();
});
//const smsServiceMock = {};
beforeEach(async () => {
const moduleRef: TestingModule = await Test.createTestingModule({
providers: [
TwilioService,
{
provide: HttpService,
useValue: {
method1: jest.fn(),
method2: jest.fn(),
method3: jest.fn(),
},
},
TwilioService,
],
imports: [
NestConfigModule.forRoot({
config: TwilioConfig,
} as Record<string, unknown>),
],
}).compile();
//getting service module from main module
httpService = moduleRef.get<HttpService>(HttpService);
client = moduleRef.get<TwilioClientCustom>(TwilioClientCustom);
service = moduleRef.get<TwilioService>(TwilioService);
});
//check service is avaible
it('Should be defined', () => {
expect(client).toBeDefined();
expect(service).toBeDefined();
expect(httpService).toBeDefined();
});
And after running test I get following errors:
Nest can't resolve dependencies of the TwilioService (?, TwilioClientCustom, HttpService). Please make sure that the argument smsTwilioConfig at index [0] is available in the RootTestModule context.
Potential solutions:
- If smsTwilioConfig is a provider, is it part of the current RootTestModule?
- If smsTwilioConfig is exported from a separate #Module, is that module imported within RootTestModule?
#Module({
imports: [ /* the Module containing smsTwilioConfig */ ]
})
How can I solve this problem ?
smsTwilioConfig is registered with Nest's IOC via SmsModule.register(opts).
However, it seems you're attempting to test TwilioService directly with createTestingModule. Which is fine, but it does mean you need include the config with a provider or import in your test.
My guess is that you thought NestConfigModule... would do that, but that's not setting the config at the right level.
I would think the following is the right direction
const moduleRef: TestingModule = await Test.createTestingModule({
providers: [
TwilioService,
{
provide: HttpService,
useValue: {
method1: jest.fn(),
method2: jest.fn(),
method3: jest.fn(),
},
},
{
// added this
provide: SMS_TWILIO_CONFIG,
useValue: testConfig
},
],
imports: [
// removed NestConfigModule
],
}).compile();

NestJS - Use service inside Interceptor (not global interceptor)

I have a controller that uses custom interceptor:
Controller:
#UseInterceptors(SignInterceptor)
#Get('users')
async findOne(#Query() getUserDto: GetUser) {
return await this.userService.findByUsername(getUserDto.username)
}
I have also I SignService, which is wrapper around NestJwt:
SignService module:
#Module({
imports: [
JwtModule.registerAsync({
imports: [ConfigModule],
useFactory: async (configService: ConfigService) => ({
privateKey: configService.get('PRIVATE_KEY'),
publicKey: configService.get('PUBLIC_KEY'),
signOptions: {
expiresIn: configService.get('JWT_EXP_TIME_IN_SECONDS'),
algorithm: 'RS256',
},
}),
inject: [ConfigService],
}),
],
providers: [SignService],
exports: [SignService],
})
export class SignModule {}
And Finally SignInterceptor:
#Injectable()
export class SignInterceptor implements NestInterceptor {
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
return next.handle().pipe(map(data => this.sign(data)))
}
sign(data) {
const signed = {
...data,
_signed: 'signedContent',
}
return signed
}
}
SignService works properly and I use it. I would like to use this as an interceptor
How can I inject SignService in to SignInterceptor, so I can use the functions it provides?
I assume that SignInterceptor is part of the ApiModule:
#Module({
imports: [SignModule], // Import the SignModule into the ApiModule.
controllers: [UsersController],
providers: [SignInterceptor],
})
export class ApiModule {}
Then inject the SignService into the SignInterceptor:
#Injectable()
export class SignInterceptor implements NestInterceptor {
constructor(private signService: SignService) {}
//...
}
Because you use #UseInterceptors(SignInterceptor) to use the interceptor in your controller Nestjs will instantiate the SignInterceptor for you and handle the injection of dependencies.

NestJS using Environment Variable

I have my environment variable in the root of the director and the following config files to import as a module.
/config/config.service.ts
// NPM Packages
import * as dotenv from 'dotenv';
import * as fs from 'fs';
import * as Joi from '#hapi/joi';
export type EnvConfig = Record<string, string>;
export class ConfigService {
private readonly envConfig: EnvConfig;
constructor(filePath: string) {
const config = dotenv.parse(fs.readFileSync(filePath));
this.envConfig = this.validateInput(config);
}
/**
* Ensures all needed variables are set,
* and returns the validated JavaScript object
* including the applied default values.
*/
private validateInput(envConfig: EnvConfig): EnvConfig {
const envVarsSchema: Joi.ObjectSchema = Joi.object({
NODE_ENV: Joi.string()
.valid('development', 'production', 'test', 'provision')
.default('development'),
PORT: Joi.number().default(3000),
MONGO_URI: Joi.required(),
API_AUTH_ENABLED: Joi.boolean().required(),
IS_AUTH_ENABLED: Joi.boolean().required(),
JWT_SECRET: Joi.required(),
JWT_EXPIRE: Joi.required(),
JWT_COOKIE_EXPIRE: Joi.required(),
});
const { error, value: validatedEnvConfig } = envVarsSchema.validate(
envConfig,
);
if (error) {
throw new Error(`Config validation error: ${error.message}`);
}
return validatedEnvConfig;
}
get(key: string): string {
return this.envConfig[key];
}
}
/config/config.module.ts
// Core Packages
import { Module } from '#nestjs/common';
// Custom Packages
import { ConfigService } from './config.service';
#Module({
providers: [
{
provide: ConfigService,
useValue: new ConfigService(
`${process.env.NODE_ENV || 'development'}.env`,
),
},
],
exports: [ConfigService],
})
export class ConfigModule {}
I have imported it globally in app.modules as follows
#Module({
imports: [ConfigModule]
})
In my database provider src/database/database.providers.ts I am trying to import the MONGO_URL variable but am getting an error.
// NPM Packages
import * as mongoose from 'mongoose';
// Custom Packages
import { envConfig } from '../config/config.service';
const mongoUrl: string = envConfig.get('MONGO_URI');
export const databaseProviders = [
{
provide: 'DATABASE_CONNECTION',
useFactory: async (): Promise<typeof mongoose> =>
await mongoose.connect(mongoUrl, {
useNewUrlParser: true,
useUnifiedTopology: true,
useCreateIndex: true,
useFindAndModify: false,
}),
},
];
Error:
Property 'get' does not exist on type 'typeof ConfigService'.ts(2339)
Here, you are importing the ConfigService class, not an instance of the ConfigService, so there is no static get method on the ConfigService class, just an instance method. What you can do instead is modify your databaseProvider to look like this:
export const databaseProviders = [
{
provide: 'DATABASE_CONNECTION',
useFactory: async (private readonly configService: ConfigService): Promise<typeof mongoose> =>
await mongoose.connect(configSerivce.get('MONGO_URI'), {
useNewUrlParser: true,
useUnifiedTopology: true,
useCreateIndex: true,
useFindAndModify: false,
}),
inject: [ConfigService],
imports: [ConfigModule]
},
];

How to change a Database connection dynamically with Request Scope Providers in Nestjs?

Working on a project with Nestjs 6.x, Mongoose, Mongo, etc...
Regarding to the Back End, in my use case, I must change the connection of one of my databases depending of some conditions/parameters coming from some requests.
Basically, I have this
mongoose.createConnection('mongodb://127.0.0.1/whatever-a', { useNewUrlParser: true })
and I want to change to, for example
mongoose.createConnection('mongodb://127.0.0.1/whatever-b', { useNewUrlParser: true })
Therefore, I have in Nestjs the first provider
export const databaseProviders = [
{
provide: 'DbConnectionToken',
useFactory: async (): Promise<typeof mongoose> =>
await mongoose.createConnection('mongodb://127.0.0.1/whatever', { useNewUrlParser: true })
}
I was researching for a while and I found out that in release Nestjs 6.x there are provider requests allowing me to modify dynamically Per-request the injection of some providers.
Anyway, I don't know how to achieve my change neither if it is going to be working in case I'd achieve that
Can anyone help or guide me?
Many thanks in advance.
You can do the following using Nest's built-in Mongoose package:
/*************************
* mognoose.service.ts
*************************/
import { Inject, Injectable, Scope } from '#nestjs/common';
import { MongooseOptionsFactory, MongooseModuleOptions } from '#nestjs/mongoose';
import { REQUEST } from '#nestjs/core';
import { Request } from '#nestjs/common';
#Injectable({ scope: Scope.REQUEST })
export class MongooseConfigService implements MongooseOptionsFactory {
constructor(
#Inject(REQUEST) private readonly request: Request,) {
}
createMongooseOptions(): MongooseModuleOptions {
return {
uri: request.params.uri, // Change this to whatever you want; you have full access to the request object.
};
}
}
/*************************
* mongoose.module.ts
*************************/
import { Module } from '#nestjs/common';
import { MongooseModule } from '#nestjs/mongoose';
import { MongooseConfigService } from 'mognoose.service';
#Module({
imports: [
MongooseModule.forRootAsync({
useClass: MongooseConfigService,
}),
]
})
export class DbModule {}
Then, you can attach whatever you want to the request and change the database per request; hence the use of the Scope.REQUEST. You can read more about Injection Scopes on their docs.
Edit: If you run into issues with PassportJS (or any other package) or the request is empty, it seems to be an error that relates to PassportJS (or the other package) not supporting request scopes; you may read more about the issue on GitHub regarding PassportJS.
I did a simple implementation for nest-mongodb,
The main changes are in mongo-core.module.ts where I store the connections in a map and used them if available instead of creating a new connection every time.
import {
Module,
Inject,
Global,
DynamicModule,
Provider,
OnModuleDestroy,
} from '#nestjs/common';
import { ModuleRef } from '#nestjs/core';
import { MongoClient, MongoClientOptions } from 'mongodb';
import {
DEFAULT_MONGO_CLIENT_OPTIONS,
MONGO_MODULE_OPTIONS,
DEFAULT_MONGO_CONTAINER_NAME,
MONGO_CONTAINER_NAME,
} from './mongo.constants';
import {
MongoModuleAsyncOptions,
MongoOptionsFactory,
MongoModuleOptions,
} from './interfaces';
import { getClientToken, getContainerToken, getDbToken } from './mongo.util';
import * as hash from 'object-hash';
#Global()
#Module({})
export class MongoCoreModule implements OnModuleDestroy {
constructor(
#Inject(MONGO_CONTAINER_NAME) private readonly containerName: string,
private readonly moduleRef: ModuleRef,
) {}
static forRoot(
uri: string,
dbName: string,
clientOptions: MongoClientOptions = DEFAULT_MONGO_CLIENT_OPTIONS,
containerName: string = DEFAULT_MONGO_CONTAINER_NAME,
): DynamicModule {
const containerNameProvider = {
provide: MONGO_CONTAINER_NAME,
useValue: containerName,
};
const connectionContainerProvider = {
provide: getContainerToken(containerName),
useFactory: () => new Map<any, MongoClient>(),
};
const clientProvider = {
provide: getClientToken(containerName),
useFactory: async (connections: Map<any, MongoClient>) => {
const key = hash.sha1({
uri: uri,
clientOptions: clientOptions,
});
if (connections.has(key)) {
return connections.get(key);
}
const client = new MongoClient(uri, clientOptions);
connections.set(key, client);
return await client.connect();
},
inject: [getContainerToken(containerName)],
};
const dbProvider = {
provide: getDbToken(containerName),
useFactory: (client: MongoClient) => client.db(dbName),
inject: [getClientToken(containerName)],
};
return {
module: MongoCoreModule,
providers: [
containerNameProvider,
connectionContainerProvider,
clientProvider,
dbProvider,
],
exports: [clientProvider, dbProvider],
};
}
static forRootAsync(options: MongoModuleAsyncOptions): DynamicModule {
const mongoContainerName =
options.containerName || DEFAULT_MONGO_CONTAINER_NAME;
const containerNameProvider = {
provide: MONGO_CONTAINER_NAME,
useValue: mongoContainerName,
};
const connectionContainerProvider = {
provide: getContainerToken(mongoContainerName),
useFactory: () => new Map<any, MongoClient>(),
};
const clientProvider = {
provide: getClientToken(mongoContainerName),
useFactory: async (
connections: Map<any, MongoClient>,
mongoModuleOptions: MongoModuleOptions,
) => {
const { uri, clientOptions } = mongoModuleOptions;
const key = hash.sha1({
uri: uri,
clientOptions: clientOptions,
});
if (connections.has(key)) {
return connections.get(key);
}
const client = new MongoClient(
uri,
clientOptions || DEFAULT_MONGO_CLIENT_OPTIONS,
);
connections.set(key, client);
return await client.connect();
},
inject: [getContainerToken(mongoContainerName), MONGO_MODULE_OPTIONS],
};
const dbProvider = {
provide: getDbToken(mongoContainerName),
useFactory: (
mongoModuleOptions: MongoModuleOptions,
client: MongoClient,
) => client.db(mongoModuleOptions.dbName),
inject: [MONGO_MODULE_OPTIONS, getClientToken(mongoContainerName)],
};
const asyncProviders = this.createAsyncProviders(options);
return {
module: MongoCoreModule,
imports: options.imports,
providers: [
...asyncProviders,
clientProvider,
dbProvider,
containerNameProvider,
connectionContainerProvider,
],
exports: [clientProvider, dbProvider],
};
}
async onModuleDestroy() {
const clientsMap: Map<any, MongoClient> = this.moduleRef.get<
Map<any, MongoClient>
>(getContainerToken(this.containerName));
if (clientsMap) {
await Promise.all(
[...clientsMap.values()].map(connection => connection.close()),
);
}
}
private static createAsyncProviders(
options: MongoModuleAsyncOptions,
): Provider[] {
if (options.useExisting || options.useFactory) {
return [this.createAsyncOptionsProvider(options)];
} else if (options.useClass) {
return [
this.createAsyncOptionsProvider(options),
{
provide: options.useClass,
useClass: options.useClass,
},
];
} else {
return [];
}
}
private static createAsyncOptionsProvider(
options: MongoModuleAsyncOptions,
): Provider {
if (options.useFactory) {
return {
provide: MONGO_MODULE_OPTIONS,
useFactory: options.useFactory,
inject: options.inject || [],
};
} else if (options.useExisting) {
return {
provide: MONGO_MODULE_OPTIONS,
useFactory: async (optionsFactory: MongoOptionsFactory) =>
await optionsFactory.createMongoOptions(),
inject: [options.useExisting],
};
} else if (options.useClass) {
return {
provide: MONGO_MODULE_OPTIONS,
useFactory: async (optionsFactory: MongoOptionsFactory) =>
await optionsFactory.createMongoOptions(),
inject: [options.useClass],
};
} else {
throw new Error('Invalid MongoModule options');
}
}
}
Check out the full implementation

Resources