NodeJsアプリケーションとモジュール間でMongodbへの接続を適切に再利用する方法


124

私は何度も読んでいますが、NodeJアプリ全体で同じデータベース(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/…–
サルバドールダリ

mongoistドライバーを確認してください。「非同期/待機を念頭に置いて構築されており、のような遅延エクスポート接続が可能module.exports = mongoist(connectionString);です。(connectionStringMongoDBマニュアルを参照してください。)
Alexandr Nil

回答:


150

mongoUtil.jsmongoに接続し、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
} );

そして、別の.jsファイルのように、他の場所でmongoにアクセスする必要がある場合は、次のようにします。

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

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

これが機能する理由は、モジュール内のノードで require D」、彼らは唯一のロード/あなただけのこれまでの1つのインスタンスになってしまいますので、一度にソースを取得_dbし、mongoUtil.getDb()常にその同じインスタンスを返します。

コードはテストされていません。


6
素晴らしい例!しかし、質問があります。複数のクラスターでアプリを実行する場合、これはどのように機能しますか?接続の別のインスタンスを起動するのでしょうか、それとも単にソースからの既存の接続を使用するのでしょうか。
Farhan Ahmad、

19
mongo接続が間に途絶えた場合、どのように対処しますか?ノードアプリケーションが再起動されるまで、getDb()へのすべての呼び出しはそのシナリオで失敗します。
アヤン

4
私はこのコードを試しましたが、mongoUtil.getDb()を実行するとnullになりました。その理由はわかりません。
Keming

3
@KemingZeng-mongoUtilを使用するすべてのモジュールがapp.jsのコールバック関数内にインポートされていることを確認する必要がありますconnectToServer。以前に設定requireした場合、他のモジュールで未定義のエラーが発生します。app.js_db
マイクR

2
mongoDBバージョン4の時点で、それは正しいはずですvar database = mongoUtil.getDb(); database.db().collection( 'users' )
Julian Veerkamp、2018

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

これは私が出会った
最も最近の

この回答はほぼ1年前であり、それ以上の情報は期待していませんが、これは私が最も使用したいアプローチのようですが、mongoファイルから構造化されていないUsersオブジェクトを引き出す運がありません。someFile.jsと非常によく似たファイルがありますが、Users.addUserを呼び出す4行目は常に爆破されます-Usersが未定義であると言います。私が見逃している明らかな部分はありますか?
ロブE.

これは私を悩ませているので、新しい質問を作成ました
Rob 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が必要ですか?接続関数は非同期関数です。エラーは、ノードスタイルのコールバックを使用して既にキャッチされています。
シャンク

1
それは私が愛する非常に観察的な質問です。コードを配置した生息地で詳しく調査しないとわかりません。コードの実行中に取ることができる経路の数は限られています。これを追加したのは、カスタムハンドラーをそこに配置できることを示すためと、デフォルトで非同期関数にtry / catchを含めるためです。それは単にフックポイントです。良い質問ですが。追加のメモを見つけたら更新します。
agm1984

getDB()を呼び出すたびに、新しい接続が作成されますよね?
Vinay Pandya

18

Expressを使用している場合は、requestオブジェクトでdb接続を取得できるようにする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'));

routes / users.js

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

8

go-olegは基本的には正しいですが、最近では(おそらく)「mongodb」自体を使用したくないので、フレームワークを使用します。

たとえば、マングースは最も一般的なものの1つです。これは、最初の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は、開発や学習のプロセスに使用される場合に優れていますが、本番環境では使用されません。これを覚えておいてください
サラスアリア2017

1
Mongooseにはスキーマも必要です。私はMonoDBパッケージをNeo4jのポリグロットパーシスタンスの一部として使用しているので、必要に応じてドキュメントプロパティを定義すると便利です。
agm1984 2017

7

promiseとして接続を初期化します。

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;

この答えは完全で機能的です。
Ahmad Sharif

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}

モンゴに接続する1つの関数と、接続のインスタンスを取得する別の関数をエクスポートします。

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モジュールのrequireを実行する必要があります。そうしないと、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というオブジェクトがあります。プロパティを添付して、ルートの内部からアクセスできます。mongo接続をapp.jsファイルにインスタンス化するだけです。

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

アプリケーションでmongooseを使用する場合は、次のスニペットを使用して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....
  }

Uは常に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
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.