Як правильно повторно використовувати з'єднання з Mongodb через додаток та модулі NodeJs


124

Я читав і читав, і все ще плутаю, який найкращий спосіб поділитися тим самим базою даних (MongoDb) підключенням у всьому додатку NodeJs. Як я розумію, з'єднання має бути відкритим при запуску та повторному використанні програми між модулями. Моя поточна ідея найкращого способу полягає в тому, що 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);. (Прочитайте connectionStringу Посібнику з MongoDB.)
Олександр Ніл

Відповіді:


151

Ви можете створити 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
} );

І тоді, коли вам потрібен доступ до монго десь ще, як в іншому .jsфайлі, ви можете зробити це:

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

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

Причина цього працює в тому, що у вузлі, коли модулі require'd, вони завантажуються / отримуються лише один раз, так що ви завжди будете мати один екземпляр _dbі mongoUtil.getDb()завжди повертатимете той самий екземпляр.

Примітка, код не перевірений.


6
Чудовий приклад! Однак у мене є питання. Як це буде працювати при запуску програми з кількома кластерами? Чи поверне це інший екземпляр з'єднання або просто використовувати наявне з'єднання з джерела?
Farhan Ahmad

19
Як би ви впоралися з тим випадком, коли з'єднання монго помирає між ними? Усі виклики до getDb () не вдалися б у тому сценарії, доки програма вузла не буде перезапущена.
Аян

4
Я спробував цей код, але мені дійшло до нуля, коли робити mongoUtil.getDb (), я не знаю, чому це.
Кемінг

3
@KemingZeng - вам потрібно переконатися, що всі модулі, які використовують mongoUtil, імпортуються в app.jsмежах функції зворотного виклику connectToServer. Якщо ви requireїх в app.jsперед тим _dbвстановлений, то ви отримаєте невизначені помилки в інших модулях.
Майк R

2
Станом на версію mongoDB 4 вона повинна бути var database = mongoUtil.getDb(); database.db().collection( 'users' ).
Julian Veerkamp

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

Це найновіший підхід, який я натрапив
KalenGi

Я усвідомлюю, що ця відповідь майже рік, і я не очікую більше інформації, але це здається таким підходом, який я найбільше хотів би використати, але мені не вистачає удачі витягнути зруйнований об’єкт Користувача з файлу mongo. У мене є файл, дуже схожий на ваш someFile.js, але рядок 4, де ви називаєте Users.addUser, завжди вибухає для мене - говорить про те, що користувачі не визначені. Чи є очевидний фрагмент, який мені не вистачає?
Роб Е.

Я врешті-решт створив нове запитання, оскільки це мене так сильно клопоче.
Роб Е.

це не повинно працювати технічно. Вимагати кешування об'єкта під час першого дзвінка. У цьому випадку він буде кешувати лише об'єкт, повернутий конструктором. Виклик "init" пізніше не впливає на те, що буде повернуто. Таким чином, цей const {Users} = вимагає ('./ mongo') повинен вийти з ладу, оскільки у кешованому результаті не буде жодної властивості "User".
beNerd

Requ.cache зберігає посилання на об'єкт, який поділяється між усіма файлами, які потребують цього об'єкта. Об'єкти, які можна мутувати діями інших частин програми (або навіть їх самих, якщо ви використовуєте таймери). Ви можете швидко протестувати, але я кинув швидку ручку на демонстрацію: codeandbox.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 }

чи потрібен спробу лову у вашому першому фрагменті? функція підключення - це функція асинхронізації. Помилка вже виявляється за допомогою зворотного виклику стилю вузла.
хвостовики

1
Це дуже спостережливе питання, яке я люблю. Я не впевнений, не вивчаючи це ближче в середовищі проживання, де ви розміщуєте код. Існує обмежена кількість шляхів, які він може пройти під час виконання коду. Я додав це здебільшого, щоб показати, що ви можете помістити туди користувальницький обробник, і тому, що я за замовчуванням включаю пробу / ловити у функції асинхронізації. Це просто точка гака. Хороше питання, хоча. Я оновлю, якщо знайдете додаткову примітку.
agm1984

кожного разу, якщо я зателефоную на getDB (), це створить нові з'єднання, правда?
Vinay Pandya

18

Якщо ви використовуєте Express, то ви можете використовувати модуль express-mongo-db, який дозволяє отримати 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 чудові, коли вони використовуються для процесу розвитку та навчання, але не для виробництва. Просто майте це на увазі
Сарас Арія

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 на зразок dbconnection.js

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. Ми можемо приєднати до нього властивості та отримати доступ до нього зсередини наших маршрутів. Ви просто встановіть своє з'єднання 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 Чесно кажучи, я не знаю жодних недоліків, але я аж ніяк не експерт з цього питання. Я виявив цей метод після дослідження, коли я виявив інші методи недоброзичливими, і мені вдалося отримати більш зрозумілий код для власної вигоди. Сподіваюся, хтось більш обізнаний, ніж я, зможе виділити, якщо є якісь недоліки. Я використовував цей метод без жодних проблем, хоча якийсь час, і, здається, він працює добре.
Хоппо

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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.