如何在NodeJs应用程序和模块之间正确重用与Mongodb的连接


124

我一直在阅读,仍然对在整个NodeJs应用程序中共享同一数据库(MongoDb)连接的最佳方法感到困惑。据我了解,应在应用启动时打开连接,并在模块之间重用。我目前的最佳方法想法是server.js(一切开始的主文件)连接到数据库并创建传递给模块的对象变量。连接后,模块代码将根据需要使用此变量,并且此连接保持打开状态。例如:

    var MongoClient = require('mongodb').MongoClient;
    var mongo = {}; // this is passed to modules and code

    MongoClient.connect("mongodb://localhost:27017/marankings", function(err, db) {
        if (!err) {
            console.log("We are connected");

            // these tables will be passed to modules as part of mongo object
            mongo.dbUsers = db.collection("users");
            mongo.dbDisciplines = db.collection("disciplines");

            console.log("aaa " + users.getAll()); // displays object and this can be used from inside modules

        } else
            console.log(err);
    });

    var users = new(require("./models/user"))(app, mongo);
    console.log("bbb " + users.getAll()); // not connected at the very first time so displays undefined

然后另一个模块models/user如下所示:

Users = function(app, mongo) {

Users.prototype.addUser = function() {
    console.log("add user");
}

Users.prototype.getAll = function() {

    return "all users " + mongo.dbUsers;

    }
}

module.exports = Users;

现在我感到这是错误的,所以这种方法有什么明显的问题吗?如果可以的话,如何使它变得更好?


我几天前也问过同样的问题。stackoverflow.com/questions/24547357/…–
萨尔瓦多·达利

检查蒙古司机。它“在构建时考虑了异步/等待 ”,并允许延迟导出连接,如module.exports = mongoist(connectionString);。(请参阅connectionStringMongoDB手册中的内容。)
Alexandr Nil,

Answers:


150

您可以创建一个mongoUtil.js模块,该模块具有连接到mongo和返回mongo数据库实例的功能:

const MongoClient = require( 'mongodb' ).MongoClient;
const url = "mongodb://localhost:27017";

var _db;

module.exports = {

  connectToServer: function( callback ) {
    MongoClient.connect( url,  { useNewUrlParser: true }, function( err, client ) {
      _db  = client.db('test_db');
      return callback( err );
    } );
  },

  getDb: function() {
    return _db;
  }
};

要使用它,您可以在app.js

var mongoUtil = require( 'mongoUtil' );

mongoUtil.connectToServer( function( err, client ) {
  if (err) console.log(err);
  // start the rest of your app here
} );

然后,当您需要访问其他位置的mongo时(例如在另一个.js文件中),可以执行以下操作:

var mongoUtil = require( 'mongoUtil' );
var db = mongoUtil.getDb();

db.collection( 'users' ).find();

之所以这样的作品是在节点,当模块是require“d,他们只得到加载/采购一次,所以你将只落得的一个实例_dbmongoUtil.getDb()总是返回同一个实例。

注意,代码未经测试。


6
很好的例子!但是,我有一个问题。在具有多个群集的应用程序上运行时,这将如何工作?它会启动连接的另一个实例还是仅使用源中的现有连接?
Farhan Ahmad 2015年

19
当mongo连接在两者之间断开时,您将如何处理此情况?在这种情况下,所有对getDb()的调用都将失败,直到重新启动节点应用程序为止。
阿扬(Ayan)

4
我尝试了这段代码,但在执行mongoUtil.getDb()时却得到了null,但我不知道为什么。
凯明

3
@KemingZeng-您需要确保将所有使用mongoUtil的模块都导入到app.js的回调函数中connectToServer。如果将require它们设置为app.jsbefore _db,则在其他模块中会得到未定义的错误。
Mike R

2
从mongoDB版本4开始,应该为var database = mongoUtil.getDb(); database.db().collection( 'users' )
朱利安·韦尔坎普

26

可以通过多种方式对此进行调整,以在适当的地方接受配置对象,但是总的来说,这与布局代码类似,尽管使用了更现代的JS语法。如果您需要的话,可以很容易地将其重写为原型和回调。

mongo.js

const { MongoClient } = require('mongodb');
const config = require('./config');
const Users = require('./Users');
const conf = config.get('mongodb');

class MongoBot {
  constructor() {
    const url = `mongodb://${conf.hosts.join(',')}`;

    this.client = new MongoClient(url, conf.opts);
  }
  async init() {
    await this.client.connect();
    console.log('connected');

    this.db = this.client.db(conf.db);
    this.Users = new Users(this.db);
  }
}

module.exports = new MongoBot();

Users.js

class User {
  constructor(db) {
    this.collection = db.collection('users');
  }
  async addUser(user) {
    const newUser = await this.collection.insertOne(user);
    return newUser;
  }
}
module.exports = User;

app.js

const mongo = require('./mongo');

async function start() {
  // other app startup stuff...
  await mongo.init();
  // other app startup stuff...
}
start();

someFile.js

const { Users } = require('./mongo');

async function someFunction(userInfo) {
  const user = await Users.addUser(userInfo);
  return user;
}

这是我遇到过的最
简洁的

我意识到这个答案已经使用了将近一年了,我真的不希望获得更多信息,但这似乎是我最想使用的方法,但是我运气很好,将变形的Users对象从mongo文件中拉出的机会为零。我有一个与您的someFile.js非常相似的文件,但是您调用Users.addUser的第4行对我来说总是很糟-说“用户”未定义。我有明显的遗漏吗?
罗伯E.

我最终提出了一个新问题,因为这困扰了我很多。
罗伯E.

从技术上讲这不起作用。Require在第一个调用中缓存对象。在这种情况下,它将仅缓存构造函数返回的对象。稍后调用“ init”对将返回的内容无效。因此,此const {Users} = require('./ mongo')应该会失败,因为缓存结果上将没有任何'User'属性。
beNerd

require.cache存储对该对象的引用,该引用在需要该对象的所有文件之间共享。可以通过程序其他部分的操作(甚至使用计时器,甚至可以通过自身)改变的对象。您可以自己快速对其进行测试,但是我很快就一起进行了演示:codesandbox.io/s/awesome-water-cexno
EddieDean

19

这是我根据go-oleg的示例使用现代语法进行的操作。矿山已经过测试并且功能正常。

我在代码中添加了一些注释。

./db/mongodb.js

 const MongoClient = require('mongodb').MongoClient
 const uri = 'mongodb://user:password@localhost:27017/dbName'
 let _db

 const connectDB = async (callback) => {
     try {
         MongoClient.connect(uri, (err, db) => {
             _db = db
             return callback(err)
         })
     } catch (e) {
         throw e
     }
 }

 const getDB = () => _db

 const disconnectDB = () => _db.close()

 module.exports = { connectDB, getDB, disconnectDB }

./index.js

 // Load MongoDB utils
 const MongoDB = require('./db/mongodb')
 // Load queries & mutations
 const Users = require('./users')

 // Improve debugging
 process.on('unhandledRejection', (reason, p) => {
     console.log('Unhandled Rejection at:', p, 'reason:', reason)
 })

 const seedUser = {
     name: 'Bob Alice',
     email: 'test@dev.null',
     bonusSetting: true
 }

 // Connect to MongoDB and put server instantiation code inside
 // because we start the connection first
 MongoDB.connectDB(async (err) => {
     if (err) throw err
     // Load db & collections
     const db = MongoDB.getDB()
     const users = db.collection('users')

     try {
         // Run some sample operations
         // and pass users collection into models
         const newUser = await Users.createUser(users, seedUser)
         const listUsers = await Users.getUsers(users)
         const findUser = await Users.findUserById(users, newUser._id)

         console.log('CREATE USER')
         console.log(newUser)
         console.log('GET ALL USERS')
         console.log(listUsers)
         console.log('FIND USER')
         console.log(findUser)
     } catch (e) {
         throw e
     }

     const desired = true
     if (desired) {
         // Use disconnectDB for clean driver disconnect
         MongoDB.disconnectDB()
         process.exit(0)
     }
     // Server code anywhere above here inside connectDB()
 })

./users/index.js

 const ObjectID = require('mongodb').ObjectID

 // Notice how the users collection is passed into the models
 const createUser = async (users, user) => {
     try {
         const results = await users.insertOne(user)
         return results.ops[0]
     } catch (e) {
         throw e
     }
 }

 const getUsers = async (users) => {
     try {
         const results = await users.find().toArray()
         return results
     } catch (e) {
         throw e
     }
 }

 const findUserById = async (users, id) => {
     try {
         if (!ObjectID.isValid(id)) throw 'Invalid MongoDB ID.'
         const results = await users.findOne(ObjectID(id))
         return results
     } catch (e) {
         throw e
     }
 }

 // Export garbage as methods on the Users object
 module.exports = { createUser, getUsers, findUserById }

您的第一个代码段中的try catch是否必要?connect函数是一个异步函数。使用节点样式回调已捕获该错误。
小腿

1
我喜欢这个非常细心的问题。如果您不仔细研究代码放置的栖息地,我不确定。在代码执行过程中可能采取的途径数量有限。我添加它主要是为了表明您可以在其中放置自定义处理程序,并且因为我默认在异步函数中包括try / catch。这只是一个挂钩点。很好的问题。如果您发现其他说明,我会进行更新。
agm1984

每次我调用getDB()都会创建新的连接,对吗?
Vinay Pandya,

18

如果使用Express,则可以使用express-mongo-db模块,该模块允许您在请求对象中获得数据库连接。

安装

npm install --save express-mongo-db

server.js

var app = require('express')();

var expressMongoDb = require('express-mongo-db');
app.use(expressMongoDb('mongodb://localhost/test'));

路线/users.js

app.get('/', function (req, res, next) {
    req.db // => Db object
});

8

go-oleg基本上是正确的,但是现在,您(可能)不想使用“ mongodb”本身,而是使用一些框架,该框架会为您做很多“肮脏的工作”。

例如,猫鼬是最常见的猫鼬之一。这就是初始server.js文件中的内容:

const mongoose = require('mongoose');
const options = {server: {socketOptions: {keepAlive: 1}}};
mongoose.connect(config.db, options);

这就是设置它所需要的一切。现在在代码中的任何地方使用它

const mongoose = require('mongoose');

然后您获得了您设置的实例 mongoose.connect


1
猫鼬是一个ORM。阅读了解为同一可能的陷阱。毫无疑问,ORM在用于发展和学习过程而不是用于生产时非常有用。请记住这一点
Saras Arya

1
猫鼬还需要架构。我正在使用MongoDB软件包作为Neo4j的多语言持久性的一部分,因此很高兴根据需要定义文档属性。
agm1984

7

初始化连接作为承诺:

const MongoClient = require('mongodb').MongoClient
const uri = 'mongodb://...'
const client = new MongoClient(uri)
const connection = client.connect() // initialized connection

然后,只要您希望对数据库执行操作,就调用该连接:

    // if I want to insert into the database...
    const connect = connection
    connect.then(() => {
        const doc = { id: 3 }
        const db = client.db('database_name')
        const coll = db.collection('collection_name')
        coll.insertOne(doc, (err, result) => {
            if(err) throw err
        })
    })

7

基于公认答案的经过测试的解决方案:

mongodbutil.js:

var MongoClient = require( 'mongodb' ).MongoClient;
var _db;
module.exports = {
  connectToServer: function( callback ) {
    MongoClient.connect( "<connection string>", function( err, client ) {
      _db = client.db("<collection name>");
      return callback( err );
    } );
  },
  getDb: function() {
    return _db;
  }
};

app.js:

var createError = require('http-errors');
var express = require('express');
var path = require('path');
var cookieParser = require('cookie-parser');
var logger = require('morgan');
var app = express();
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'ejs');
app.use(logger('dev'));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));

var mongodbutil = require( './mongodbutil' );
mongodbutil.connectToServer( function( err ) {
  //app goes online once this callback occurs
  var indexRouter = require('./routes/index');
  var usersRouter = require('./routes/users');
  var companiesRouter = require('./routes/companies');
  var activitiesRouter = require('./routes/activities');
  var registerRouter = require('./routes/register');  
  app.use('/', indexRouter);
  app.use('/users', usersRouter);
  app.use('/companies', companiesRouter);
  app.use('/activities', activitiesRouter);
  app.use('/register', registerRouter);  
  // catch 404 and forward to error handler
  app.use(function(req, res, next) {
    next(createError(404));
  });
  // error handler
  app.use(function(err, req, res, next) {
    res.locals.message = err.message;
    res.locals.error = req.app.get('env') === 'development' ? err : {};
    res.status(err.status || 500);
    res.render('error');
  });
  //end of calback
});

module.exports = app;

Activities.js-路线:

var express = require('express');
var router = express.Router();
var mongodbutil = require( '../mongodbutil' );
var db = mongodbutil.getDb();

router.get('/', (req, res, next) => {  
    db.collection('activities').find().toArray((err, results) => {
        if (err) return console.log(err)
            res.render('activities', {activities: results, title: "Activities"})
    });
});

router.post('/', (req, res) => {
  db.collection('activities').save(req.body, (err, result) => {
    if (err) return console.log(err)
    res.redirect('/activities')
  })
});

module.exports = router;

该答案是完整且实用的。
艾哈迈德·谢里夫

7

这是我在2020年的设置:

./utils/database.js

const { MongoClient } = require('mongodb');

class Mongo {
    constructor () {
        this.client = new MongoClient("mongodb://127.0.0.1:27017/my-app", {
            useNewUrlParser: true,
            useUnifiedTopology: true
        });
    }

    async main () {
        await this.client.connect();
        console.log('Connected to MongoDB');

        this.db = this.client.db();
    }
}

module.exports = new Mongo();

/app.js

const mongo = require('./utils/database');
const express = require('express');

const app = express();

const boot = async () => {
    await mongo.main();
    app.listen(3000);
};

boot();

3

我们可以创建dbconnection.js之类的dbconnection文件

const MongoClient = require('mongodb').MongoClient
const mongo_url = process.env.MONGO_URL;

    module.exports = {
        connect: async function(callback) {
            var connection;
            await new Promise((resolve, reject) => {
                MongoClient.connect(mongo_url, {
                    useNewUrlParser: true
                }, (err, database) => {
                    if (err)
                        reject();
                    else {
                        connection = database;
                        resolve();
                    }
                });
            });
            return connection;
        }

    };

然后在您的应用中使用该文件,例如

var connection = require('../dbconnection');

然后在异步函数中使用这样的代码

db  = await connection.connect();

希望这会工作


2

我为此有些迟,但是我也会添加解决方案。与这里的答案相比,这是一种更加笨拙的方法。

无论如何,如果您使用的是MongoDB 4.0版和Node.js 3.0(或更高版本),则可以使用中的isConnected()功能MongoClient

const MongoClient = require('mongodb').MongoClient;
const uri = "<your connection url>";
const client = new MongoClient(uri, { useNewUrlParser: true });

if (client.isConnected()) {
  execute();
} else {
  client.connect().then(function () {
    execute();
  });
}

function execute() {
    // Do anything here
    // Ex: client.db("mydb").collection("mycol");
}

这对我来说很好。希望能帮助到你。


2

我来晚了,但是希望这个答案对某人有帮助,这是一个功能代码:

db.js

const MongoClient = require("mongodb").MongoClient
const urlMongo = "mongodb://localhost:27017"

var db;

function connectToServer( callback ) {
    MongoClient.connect(urlMongo,  { useUnifiedTopology: true , useNewUrlParser: true }, function( err, client ) {
        db  = client.db('auth');
        return callback( err );
    })
}

function getDb() {
    return db
}

module.exports = {connectToServer, getDb}

我们导出一个函数以连接到mongo,然后导出另一个函数以获取连接的实例。

app.js

const express = require('express')
const app = express()

const mongo = require('./db.js');

mongo.connectToServer( function( err) {
  if (err) console.log(err);
  const auth = require('./modulos')

  app.post('/login', (req, res) => { auth.login(req, res)})
  app.listen(3000, function () { console.log('Corriendo en puerto 3000')})

});

初始化连接后,必须执行auth模块的需求,否则getDb函数将返回未定义。

module.js

const db = require('../db.js').getDb()
const usuariosCollection = db.collection('usuarios')

function login(req, res){
    usuariosCollection.find({ 'username': 'Fran' }).toArray(function (err, doc) {
        ...
    })
}

2

正如Express标记的那样,我想我会提到Express具有内置功能,可在路线之间共享数据。有一个名为app.locals的对象。我们可以将属性附加到它并从我们的路线内部访问它。您只需在app.js文件中实例化mongo连接。

var app = express();

MongoClient.connect('mongodb://localhost:27017/')
.then(client =>{
  const db = client.db('your-db');
  const collection = db.collection('your-collection');
  app.locals.collection = collection;
});
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              // view engine setup
app.set('views', path.join(__dirname, 'views'));

现在可以在下面的路由中访问此数据库连接,而无需创建和需要其他模块。

app.get('/', (req, res) => {
  const collection = req.app.locals.collection;
  collection.find({}).toArray()
  .then(response => res.status(200).json(response))
  .catch(error => console.error(error));
});

此方法确保您在应用程序运行期间一直打开数据库连接,除非您选择随时关闭它。它易于使用,req.app.locals.your-collection不需要其他模块。


我发现这是最干净的方法。这种方法是否有任何弊端?我正在使用它,对我来说看起来不错,可以分享我的经验。
Priya Ranjan Singh

@PriyaRanjanSingh老实说,我不知道有什么弊端,但我绝不是专家。经过研究,我发现了这种方法,因为我发现了其他方法,并且为了自己的利益,我正在寻找更清晰易懂的代码。希望比我本人更有学识的人能够指出是否存在任何缺点。虽然我使用这种方法已经有一段时间了,但它已经存在了一段时间了,而且似乎工作得很好。
Hoppo

1

如果您选择在应用程序中使用猫鼬,请使用以下代码段编辑app.js文件

app.js

const mongoose = require('mongoose');
mongoose.connect('mongodb://localhost:27017/Your_Data_Base_Name', {useNewUrlParser:true})
  .then((res) => {
    console.log(' ########### Connected to mongDB ###########');
  })
  .catch((err) => {
    console.log('Error in connecting to mongoDb' + err);
  });`

下一步: 为您的应用程序定义模型需要它们,然后直接执行CRUD操作

blogSchema.js

 const mongoose = require('mongoose');
 const Schema = mongoose.Schema;
 const blogSchema = new Schema({
     _id : mongoose.Schema.Types.ObjectId,
     title : {
        type : 'String',
        unique : true,
        required : true       
    },
    description : String,
        comments : [{type : mongoose.Schema.Types.ObjectId, ref: 'Comment'}]
 });
 module.exports = mongoose.model('Blog', blogSchema);

用法 createBlog.js

const Blog = require('../models/blogSchema');
exports.createBlog = (req, res, next) => {
const blog = new Blog({
  _id : new mongoose.Types.ObjectId,
  title : req.body.title,
  description : req.body.description,
});
blog.save((err, blog) => {
  if(err){
    console.log('Server Error save fun failed');
    res.status(500).json({
      msg : "Error occured on server side",
      err : err
    })
  }else{
    //do something....
  }

您不必总是连接到mogoDB ....


1
var MongoClient = require('mongodb').MongoClient;
var url = 'mongodb://localhost:27017/';
var Pro1;

module.exports = {
    DBConnection:async function()
    {
        Pro1 = new Promise(async function(resolve,reject){
            MongoClient.connect(url, { useNewUrlParser: true },function(err, db) {
                if (err) throw err;
                resolve(db);
            });        
        });
    },
    getDB:async function(Blockchain , Context)
    {
        bc = Blockchain;
        contx = Context;
        Pro1.then(function(_db)
        {
            var dbo = _db.db('dbname');
            dbo.collection('collectionname').find().limit(1).skip(0).toArray(function(err,result) {
                if (err) throw err;
                console.log(result);
            });
        });
    },
    closeDB:async function()
    {
        Pro1.then(function(_db){
            _db.close();
        });
    }
};

1
能否请您添加简短说明?
RtmY

1
const express = require('express')
const server = express()
const mongoClient = require('./MongoDB.js').client
const port = 3000
;(async () => {
    await mongoClient.connect()
    server.listen(port, () => console.log(`Server is listening on port ${port}!`))
})().catch(console.error)

0

我发现这很好用:)

mongoUtil.ts

import { MongoClient } from 'mongodb';
const uri =
  'MONGOSTRING';

let connPoolPromise: any = null;

const mongoPoolPromise = () => {
  if (connPoolPromise) return connPoolPromise;

  connPoolPromise = new Promise((resolve, reject) => {
    const conn = new MongoClient(uri, {
      useNewUrlParser: true,
      useUnifiedTopology: true,
    });

    if (conn.isConnected()) {
      return resolve(conn);
    } else {
      conn
        .connect()
        .then(() => {
          return resolve(conn.db('DATABASENAME'));
        })
        .catch(err => {
          console.log(err);
          reject(err);
        });
    }
  });

  return connPoolPromise;
};

export = {
  mongoPoolPromise,
};

anyFile.ts

const { mongoPoolPromise } = require('./mongoUtil');

async function getProducts() {
  const db = await mongoPoolPromise();
  const data = await db
    .collection('myCollection')
    .find({})
    .toArray();
  console.log(data);
  return data;
}

export { getProducts };

答案标记有javascript,认为TypeScript答案不合适。
KPopOG
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.