Exploring the usage of asynchronous providers in NestJS

I am currently utilizing nestjs and I am interested in creating an async provider. Below is my folder structure:

.
├── dist
│   └── main.js
├── libs
│   └── dma
│       ├── src
│       │   ├── client
│       │   │   ├── client.module.ts
│       │   │   ├── client.service.spec.ts
│       │   │   └── client.service.ts
│       │   ├── cmts
│       │   │   ├── cmts.module.ts
│       │   │   ├── cmts.service.spec.ts
│       │   │   └── cmts.service.ts
│       │   ├── dma.module.ts
│       │   └── index.ts
│       └── tsconfig.lib.json
├── nest-cli.json
├── package.json
├── package-lock.json
├── README.md
├── src
│   ├── app.controller.spec.ts
│   ├── app.controller.ts
│   ├── app.module.ts
│   ├── app.service.ts
│   └── main.ts
├── test
│   ├── app.e2e-spec.ts
│   └── jest-e2e.json
├── tsconfig.build.json
└── tsconfig.json

My intention is to designate the ClientService as an async provider.

  • I want the asynchronous operation of the database connection to execute first.
  • After that, any dependent provider can begin invoking the sendSql() method.

Below are the files involved:

~/src/app.module.ts

import { Module } from '@nestjs/common'
import { AppController } from './app.controller'
import { AppService } from './app.service'
import { DmaModule } from '@app/dma'

@Module({
  imports: [DmaModule],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}

~/src/app.service.ts

import { CmtsService } from '@app/dma/cmts/cmts.service'
import { Injectable } from '@nestjs/common'

@Injectable()
export class AppService {
  constructor(private readonly cmtsService: CmtsService) {}
  getHello(): string {
    return 'Hello World!'
  }
}

~/libs/dma/client/client.module.ts

import { Module } from '@nestjs/common'
import { ClientService } from './client.service'

@Module({
  providers: [ClientService],
  exports: [ClientService],
})
export class ClientModule {}

~/libs/dma/client/client.service.ts

import { Injectable } from '@nestjs/common'

@Injectable()
export class ClientService {
  dbConnection: any;
  async connectToDb(){
    // connection logic...
    this.dbConnection = theConnectionObject;
  }
  async sendSql(sql: string){
   const result = await dbConnection.send(sql) // for example
   return result
  }
}

~/libs/dma/cmts/cmts.module.ts

import { Module } from '@nestjs/common'
import { CmtsService } from './cmts.service'
import { ClientModule } from '../client/client.module'

@Module({
  imports: [ClientModule],
  providers: [CmtsService],
  exports: [CmtsService],
})
export class CmtsModule {}

~/libs/dma/cmts.service.ts

import { Injectable } from '@nestjs/common'
import { ClientService } from '../client/client.service'

@Injectable()
export class CmtsService {
  constructor(private readonly clientService: ClientService) {}
}

The nestjs documentation mentions the following: here

It suggests this approach:

{
  provide: 'ASYNC_CONNECTION',
  useFactory: async () => {
    const connection = await createConnection(options);
    return connection;
  },
}

However, I am concerned about whether it is advisable to directly create an instance from the provider:

{
  provide: 'ASYNC_CONNECTION',
  useFactory: async () => {
    const clientService = new ClientService() // 👎 bad practice (?)
    const connection = await clientService.connectDb();
    return connection;
  },
}

Answer №1

Within the file named database.providers.ts, you will find the code responsible for initializing the database connection

 export const databaseProviders = [
  {
    provide: 'DbConnectionToken',
    useFactory: async (
      environmentSettingsService: EnvironmentSettingsService
    ): Promise<typeof mongoose> => {
      mongoose.set({ strict: false });
      return await mongoose.connect(
        process.env.isApiTestRunning
          ? testDatabaseUrl
          : environmentSettingsService.config.get('usersdb')
      );
    },
    inject: [EnvironmentSettingsService],
  },
];

In the class named MyAppProviders, I define the schema token provider for injecting the schema with the established connection in MyAppProviders.ts

 export const MyAppProviders = [
  {
    provide: modelTokens.UsersToken,
    useFactory: (connection: Connection) => connection.model(dbCollections.users, UsersSchema),
    inject: ["DbConnectionToken"],
  },

It is necessary to initialize these providers within your modules, assuming you have prior knowledge of this process

When utilizing these schema provider tokens in services, I demonstrate below in one of my service classes how to inject the user model for performing database operations on the users collection

Injectable()
export class UserCrud {
    private similaritySearch: any;

    constructor(
        @Inject(modelTokens.UsersToken)
        private usersModel: Model<any>)
    {}
// Your code
//using userModel
await this.usersModel.findOne({}).....
....
}

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

What is the best way to send various parameters to a component using [routerLink] or router.navigate?

My app-routing.module.ts is configured as shown below: const routes: Routes = [ { path: "branches/:branch", component: BranchesComponent }, // ... ]; In addition, in my app.component.html, I have the following code: <li> ...

Angular - Retrieve Excel file with its current filename

Is there a way for me to save an Excel file with its original filename from a service in my Angular app using the file-saver library? Below is my current code: let blob = new Blob([response], {type: 'application/vnd.openxmlformat-officedocument.spre ...

Creating a map in Typescript initialized with a JSON object

In my Typescript class, there is a map that I'm trying to initialize: public map:Map<string,string>; constructor() { let jsonString = { "peureo" : "dsdlsdksd" }; this.map = jsonString; } The issue I'm encounte ...

Error message in Typescript: The argument type '() => () => Socket<DefaultEventsMap, DefaultEventsMap>' cannot be assigned to a parameter of type 'EffectCallback'

I am struggling to comprehend how I should specifically type constrain in order to prevent the error within my useEffect. One approach is to either cast my newSocket or specify the return value of my useEffect as any, but I am hesitant about that solution. ...

Guide to displaying the continent name on a 3D globe using Reactjs, TypeScript, and Threejs

I am currently working on integrating Threejs into my Nextjs 14 application to create a 3D earth globe using Gltf. However, I am facing an issue where I am unable to display the text of the continent name on their respective continents. I want to have fixe ...

There are no properties associated with this particular data type

As someone who is new to TypeScript, I am encountering two issues with data types. This snippet shows my code: const say: object = { name: "say", aliases: [""], description: "", usage: "", run: (client: ob ...

Is there a method to ensure the strong typing of sagas for dispatching actions?

Within redux-thunk, we have the ability to specify the type of actions that can be dispatched enum MoviesTypes { ADD_MOVIES = 'ADD_MOVIES', } interface AddMoviesAction { type: typeof MoviesTypes.ADD_MOVIES; movies: MovieShowcase[]; } typ ...

VPS mysteriously terminates TypeScript compilation process without any apparent error

I've encountered an issue while trying to compile my TypeScript /src folder into the /dist folder. The process works smoothly on my local machine, but when I clone the repo onto my VPS (Ubuntu Server 22.04), install npm, and run the compile script, it ...

What is the best way to merge imported types from a relative path?

In my TypeScript project, I am utilizing custom typings by importing them into my .ts modules with the following import statement: import { MyCoolInterface } from './types' However, when I compile my project using tsc, I encounter an issue wher ...

Form appears outside the modal window

I am facing an issue with my modal where the form inside is displaying outside of the modal itself. Despite trying to adjust the CSS display settings and switching to react-bootstrap from regular bootstrap, the problem persists. I am uncertain about what s ...

Encountering error 2307 "Cannot find module" when using Vue 3 with script setup and TypeScript

I am currently attempting to run unit tests in my Vue3 project using vue/test-utils and jest. Upon running the npm run test script, the test fails due to an error with the import: error TS2307: Cannot find module 'pathtofile/file.vue' I have tr ...

The absence of a semicolon following the interface declaration is the issue

I am facing a slight issue with ESLint and Typescript, particularly regarding semicolons after declaring interfaces. Additionally, I utilize VSCode as my editor with automatic formatting upon saving. Below is the configuration in my .eslintrc.json file: ...

Missing from the TypeScript compilation are Angular5's polyfills.ts and main.ts files

Here is the structure of my Angular5 project. https://i.stack.imgur.com/tmbE7.png Within both tsconfig.app.json and package.json, there is a common section: "include": [ "/src/main.ts", "/src/polyfills.ts" ] Despite trying various solu ...

Why is my custom 404 page failing to load after building my Next.js application?

I recently set up a custom 404 page for my Next.js app and wanted to test it locally before deploying to the server. To do this, I used the "serve" package to host the project on my local machine. However, when I tried navigating to a non-existent page, th ...

Angular's strict mode causing issues with CustomValidator for email field in form

Trying to correctly define an email form field: this.form.addControl('email', new FormControl('', [ Validators.required, CustomFormValidators.isValidEmail ])); Utilizing a CustomFormValidators class that includes an isValidEmail me ...

Is there a way to establish a "transient" category within a category to maintain code efficiency?

I'm struggling to figure out how to search for this specific question on Stack Overflow. The structure of my generic type FetchOptions looks like this: type FetchOptions<T> = { explode?: string & keyof T | (string & keyof T)[]; } I&a ...

find the element in cypress with multiple child elements

Looking for a way to target the first HTML element that contains more than 2 children. Another option is to access the children elements of the first parent element. <div class="market-template-2-columns"> <button type="button&q ...

The type 'Element | undefined' cannot be assigned to the type 'ReactElement<any, any> | null'

Important Note about Components and Files: Explanation of types.ts File: export interface IIcon { iconName: string; iconSize: string; iconFill: string; } Clarification regarding index.tsx File: import React, { FC } from 'react'; import { ...

Issue detected: Props that are of type Object/Array must utilize a factory function in order to provide the default value

I recently started using Vue-Cli3.0 and came across this interesting module for Vue.js called https://github.com/holiber/sl-vue-tree It's a customizable draggable tree component for Vue.js, but I encountered an issue where it couldn't copy funct ...

Modify the empty message for the PrimeNG multiselect component

Is there a way to customize the message 'no results found' in p-multiselect on Angular? I have successfully changed the default label, but I am struggling to find a solution to override the empty message. Here is my code snippet: <p-multiSel ...