如何在 Nestjs 中创建 mongodb 连接提供程序

Posted

技术标签:

【中文标题】如何在 Nestjs 中创建 mongodb 连接提供程序【英文标题】:How to create a mongodb connection provider in Nestjs 【发布时间】: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 包省略这些开销。 【参考方案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;
      
    ,
  
]

【讨论】:

这个问题一年多以前就已经回答过了。请不要添加不能改善现有答案的答案。

以上是关于如何在 Nestjs 中创建 mongodb 连接提供程序的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 typeORM 在 Nestjs 中创建类型为 Date 的列并键入 DateTime?

部署在 NX monorepo 中创建的 nestjs 服务器

通过 java 应用程序连接在 mongolab 中创建的 mongodb

通过 java 应用程序连接在 mongolab 中创建的 mongodb

在nestJs中注入mongodb连接不起作用

NestJs / Mongoose 在 Docker 撰写时无法建立 MongoDB 数据库连接