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

时间:2014-07-08 00:22:26

标签: javascript node.js mongodb express

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

    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;

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

18 个答案:

答案 0 :(得分:124)

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

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时,它们只会加载/获取一次,因此您最终只会得到_db的一个实例mongoUtil.getDb()将始终返回相同的实例。

注意,代码未经过测试。

答案 1 :(得分:17)

以下是基于go-oleg的例子,我如何使用现代语法来做到这一点。我的测试和功能。

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

  

./分贝/ 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()
 })
  

./用户/ 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 }

答案 2 :(得分:16)

如果您使用的是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
});

答案 3 :(得分:8)

go-oleg基本上是正确的,但在这些日子里你(可能)不想使用&#34; mongodb&#34;本身,而是使用一些框架,这将做很多&#34;脏工作&#34;对你而言。

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

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

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

const mongoose = require('mongoose');

您可以使用mongoose.connect

设置该实例

答案 4 :(得分:6)

将连接初始化为承诺:

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
        })
    })

答案 5 :(得分:6)

这是我在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();

答案 6 :(得分:4)

基于已接受答案的经过测试的解决方案:

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 :(得分:4)

因为这是用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可以轻松访问它,并且不需要其他模块。

答案 8 :(得分:4)

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

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函数将返回undefined。

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) {
        ...
    })
}

答案 9 :(得分:3)

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

mongo.js

from mlarray import double, single, uint8, ...

Users.js

loky

app.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();

someFile.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;

答案 10 :(得分:2)

我们可以创建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();

希望这会起作用

答案 11 :(得分:2)

如果您选择在应用程序中使用猫鼬,请使用以下代码段编辑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 ....

答案 12 :(得分: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();
        });
    }
};

答案 13 :(得分:1)

<!DOCTYPE html>
<html>

   <head>
      <title>HTML Tables</title>
   </head>

   <body>
      <table border = "1">
         <tr>
            <td><p><span>Row 1, Column 1, This should be kept because it has more than two tokens</span></p></td>
            <td><p><span>not kept</span></p></td>
         </tr>

         <tr>
            <td><p><span>Row 2, Column 1, should be kept</span></p></td>
            <td><p><span>Row 2, Column 2, should be kept</span></p></td>
         </tr>
      </table>

   </body>
</html>

答案 14 :(得分:1)

我发现这很好用:)

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 };

答案 15 :(得分:1)

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

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

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");
}

这对我来说很好。希望对您有所帮助。

答案 16 :(得分:0)

基于公认的答案,我使用一种简单的方法。但是,仅当您要在一段时间后执行的内部函数中使用db时,才使用此函数。例如:在快速航线功能中,这是您可以采用的最简单的方法。

mongo.js

const MongoClient = require("mongodb").MongoClient

var db

const connectDb = (callback) => {
    if (db) return callback()
    MongoClient.connect( uri, {ops}, 
        (err, database) => {
            if (err) return console.log(err)
            db = database.db("dbName") 
            console.log("Database Connected")
            callback()
        }
    )
}

const getDb = (collectionToGet) => {
    return db.collection(collectionToGet)
}

module.exports = {
    connectDb,
    getDb,
}

现在,在需要db对象的其他文件中,

user.js

const { connectDb, getDb } = require('mongo.js')

var db // store db object in this object
connectDb(() => ( db = getDb("user") ))

app.get('/', (req, res) => {
    // do something with req 
    db.insert({})
    // do something with res
}

答案 17 :(得分:0)

您可以使用Singleton Design Pattern来实现MongoDB连接的跨文件使用。

Init.mjs

/* ################ Controller ################ */
import ctrlLib from '../../controller/lib.mjs';

/* ################ MongoDB ################ */
import mongodb from 'mongodb';

/* ################ Logs ################ */
import log from 'fancy-log';
import chalk from 'chalk';


/** Init MongoDB connection */
export class Init {
  /**
   * Check if its first time usage of this class.
   If true set class instance to this that we always get same instance.
   * Then get MongoDB details from config.yml and set as global.
   * In the last step we return the class instance.
  */
  constructor() {
    if (Init.instance == null) Init.instance = this;

    const config = ctrlLib.getConfig();
    this.MongoURL = config.MongoDB.url;
    this.MongoName = config.MongoDB.dbname;
    ({MongoClient: this.MongoClient} = mongodb);

    return Init.instance;
  }; // constructor(){


  /** Connect to Database and return connection */
  async connect() {
    try {
      const client = await this.MongoClient.connect(
          this.MongoURL, {useNewUrlParser: true, useUnifiedTopology: true},
      );

      this.connection = {'db': client.db(this.MongoName), 'client': client};
      return this.connection;
    } // try {
    catch (e) {
      log( `${chalk.red.bold('❌ ERROR')} while try to connect to MongoDB DB
      ${chalk.white.bold('Error:\n')} ${e}` );
    } // catch (e) {
  }; // async connect() {


  /**
   * Return connection for cross file usage
   * @return {object}
  */
  getConnection() {return this.connection;};
}; // export class Init {

app.mjs

  • 确保以1倍的时间在项目内部的任何地方创建MongoDB连接,以便以后可以在其他文件中使用它。
/* ################ Services ################ */
import {Init} from './Init.mjs';

(async ()=>{
  await new Init().connect();
})().catch(e=>{log('app.mjs - Catch error: ' + e);});

anyOtherFile.mjs

/* ################ Services ################ */
import {Init} from './Init.mjs';

/** Subclass of Search which contains lib functions */
class Lib {
  /**
   * Find data by using search query and return result.
   * @param {string} collection - Name of collection
   * @param {object} query - Search query
  */
  async findOne(collection, query) {
    const connection = new Init().getConnection();
    return await connection.db.collection(collection).findOne(query);
  }; // async findOne() {
}; // class Lib {