【问题标题】:How to create a mongodb connection provider in Nestjs如何在 Nestjs 中创建 mongodb 连接提供程序
【发布时间】:2020-06-06 07:34:35
【问题描述】:

我正在尝试在 Nestjs 中为 MongoDB 创建一个数据库连接提供程序。

我通过放置断点检查了user.controller.ts 和 mongoDbProvider,发现控制器在建立数据库连接之前获取。如何在控制器初始化之前建立数据库连接?

Nest.js 文档说 useFactory 方法将在任何其他依赖它的模块之前运行。

src/mongo-db/mongodb.provider.ts

import { MongoClient } from "mongodb";
import { MONGODB_PROVIDER } from "../constants";

export const mongoDbProviders = [
  {
    provide: MONGODB_PROVIDER,
    useFactory: async () => {
      MongoClient.connect('mongodb://localhost:27017',
        { useUnifiedTopology: true },
        (error, client) => {
          return client.db('nestjs-sample');
        });
    }
  },

];

src/mongo-db/mongo-db.module.ts

import { mongoDbProviders } from './mongo-db.providers';

@Module({
  providers: [...mongoDbProviders],
  exports: [...mongoDbProviders],
})
export class MongoDbModule {

}

src/constants.ts

export const MONGODB_PROVIDER = 'MONGODB_CONNECTION';

我将MongoDbModule 导入到user.module.ts

src/user/user.module.ts

import { Module } from '@nestjs/common';
import { UserController } from './user.controller';
import { UserService } from './user.service';
import { MongoDbModule } from 'src/mongo-db/mongo-db.module';

@Module({
  imports: [MongoDbModule],
  controllers: [UserController],
  providers: [UserService]
})
export class UserModule {}

在这里,我将 dbmongoDbProvider 注入到 UserController 构造函数中。但是构造函数在 db 连接之前运行。

src/user/user.controller.ts

import { Controller, Post, Req, Get, Res, Inject } from '@nestjs/common';
import { Request, Response } from "express";
import { MONGODB_PROVIDER } from 'src/constants';

@Controller('users')
export class UserController {

  constructor(@Inject(MONGODB_PROVIDER) private readonly db: any) {

  }

  @Post()
  async create(@Req() request: Request, @Res() response: Response) {
    this.db.collection('users').insertOne(request.body, (err, result) => {
      if (err) {
        response.status(500).json(err);
      } else {
        response.status(201);
        response.send(result);
      }
    });
  }

  @Get()
  get(@Req() request: Request, @Res() response: Response) {
    response.status(400).json({
      message: 'kidilam service'
    });
  }

}

【问题讨论】:

  • 因此,此解决方案包含大量开销,您可以使用即用型和可用的开箱即用专用 @nestjs/mongoose 包省略这些开销。

标签: node.js mongodb typescript nestjs


【解决方案1】:

我找到了解决方案。这是因为 useFactory 需要一个承诺,因为它是一个 async 函数。所以我将MongoClient.connect() 包裹在Promise 中以解析数据库连接。现在它会等到 promise 解决后才初始化任何依赖它的模块。

import { MongoClient } from "mongodb";
import { MONGODB_PROVIDER } from "../constants";

export const mongoDbProviders = [
  {
    provide: MONGODB_PROVIDER,
    useFactory: async () => new Promise((resolve, reject) => {
      MongoClient.connect('mongodb://localhost:27017',
      { useUnifiedTopology: true },
      (error, client) => {
        if (error) {
          reject(error);
        } else {
          resolve(client.db('nestjs-sample'));
        }
      });
    })
  },
];

【讨论】:

    【解决方案2】:

    是因为调用回调后,返回的连接对象基本丢失了。如果您必须使用工厂提供程序,您可以尝试在 MongoClient.connect 的回调中使用闭包以及值提供程序。我的意思是这样的方法:

    import { MongoClient } from "mongodb";
    import { MONGODB_PROVIDER } from "../constants";
    
    const MONGODB_PROVIDER_RESOLVED = 'MONGODB_PROVIDER_RESOLVED'
    let connection = undefined;
    export const mongoDbProviders = [
      {
        provide: MONGODB_PROVIDER_RESOLVED,
        useValue: connection
      },
      {
        provide: MONGODB_PROVIDER,
        useFactory: async () => {
          MongoClient.connect('mongodb://localhost:27017',
            { useUnifiedTopology: true },
            (error, client) => {
              connection = client.db('nestjs-sample');
            });
    
        }
      },
    
    ];
    

    然后注入 MONGODB_PROVIDER 和 MONGODB_PROVIDER_RESOLVED:

    constructor(@Inject(MONGODB_PROVIDER) private readonly _db: any, @Inject(MONGODB_PROVIDER) private readonly db: any) 
    

    第一次依赖注入将强制你的工厂代码运行。第二个将保持已解析的连接。这有点笨拙,我同意。使用类提供者可能会更好:

    import { Injectable } from '@nestjs/common';
    @Injectable()
    export class MongoDBProvider {
        connection
        constructor(){
          MongoClient.connect('mongodb://localhost:27017',
                { useUnifiedTopology: true },
                ((error, client) => {
                  this.connection = client.db('nestjs-sample');
                }).bind(this));
        }
    }
    

    您现在可以在控制器中使用它:

    constructor( private readonly db: MongoDBProvider) {
    
      }
    

    【讨论】:

    • 我不能使用类提供程序,因为类MongoDbProvider 将调用构造函数,但不会等到建立数据库连接。所以控制器中的db 对象将为空。
    【解决方案3】:

    这是我使用 NodeJS 原生驱动程序和 async/await 连接到 MongoDB 的方法:

    import { MongoClient, Db } from 'mongodb';
    
    export const databaseProviders = [
      {
        provide: 'DATABASE_CONNECTION',
        useFactory: async (): Promise<Db> => {
          try {
            const client = await MongoClient.connect('mongodb://localhost:27017', {
              useUnifiedTopology: true,
            });
    
            return client.db('my_db');
          } catch (e) {
            throw e;
          }
        }
      },
    ];
    

    在我写的这篇文章中有一个完整的实现https://medium.com/@gusiol/nestjs-with-mongodb-native-driver-9d82e377d55

    【讨论】:

      【解决方案4】:

      // We can also use MongoClient in nestJs using DipendencyInjection
      // first we have to create database connection 
      
      // fileName :- db/dbConnection.service.ts
      
              import { Injectable } from '@nestjs/common';
              import { MongoClient } from 'mongodb';
              import { config } from 'dotenv';
              config();
      
              let dbInstance;
              const url = process.env.MONGO_URL || 'mongodb://localhost:27017/SocialUserEr';
      
              @Injectable()
              export class DbConnection {  constructor() {
                  this.connect();
                }
                connect() {
                  const client = new MongoClient(url);
                  client
                    .connect()
                    .then((connection) => {
                      dbInstance = connection.db();
                      console.log('Database connection Succeeded');
                    })
                    .catch((err) => {
                      console.log(err);
                    });
                }
                db() {
                  if (dbInstance) return dbInstance;
                }
              }
      
      Then we have to export it for use anywhere: ex
      
      // fileName:- db/db.module.ts
      
              import { Module } from '@nestjs/common';
              import { DbConnection } from './db.service';
      
              @Module({
                imports: [],
                exports: [DbConnection],
                providers: [DbConnection],
              })
              export class DbModule {}
      
           
      // for Using this connection inside a module you have to import DbModule : ex
      // In this module we want to use db connection so we have to import db module
      
      // fileName:- auth/auth.module.ts
      
      import { Module } from '@nestjs/common';
      import { DbModule } from 'src/db/db.module';
      import { AuthController } from './auth.controller';
      import { AuthService } from './auth.service';
      
      @Module({
        imports: [DbModule],
        controllers: [AuthController],
        providers: [AuthService],
      })
      export class AuthModule {}
      
      // And lastly for using inside any module you have to just 
      // fileName :- auth.service.ts
      
      import { Injectable } from '@nestjs/common';
      import { DbConnection } from 'src/db/db.service';
      
      @Injectable()
      export class AuthService {
        constructor(private database: DbConnection) {}
        async createUser(userInfo) {
          console.log(userInfo);
          return await this.database.db().collection('user').insertOne(userInfo);
        }
      }
      
      <!-- begin snippet: js hide: false console: true babel: false -->

      【讨论】:

      • 您的答案可以通过额外的支持信息得到改进。请edit 添加更多详细信息,例如引用或文档,以便其他人可以确认您的答案是正确的。你可以找到更多关于如何写好答案的信息in the help center
      【解决方案5】:

      就让你useFactoryawait

      export const databaseProviders = [
        {
          provide: "MAIN_DATABASE_CONNECTION",
          useFactory: async (): Promise<mongodb.Db> => {
            try {
              const client = await mongodb.MongoClient.connect(process.env.CLUSTER, {
              });
              const db = client.db("dbName");
              return db;
            } catch (error) {
              throw error;
            }
          },
        }
      ]
      

      【讨论】:

      • 这个问题一年多以前就已经回答过了。请不要添加不能改善现有答案的答案。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-11-17
      • 2021-11-07
      • 1970-01-01
      • 1970-01-01
      • 2020-07-22
      • 2019-02-05
      • 2021-05-30
      相关资源
      最近更新 更多