Previously I had a working, single-file, application that was essentially:
const express = require('express');
const app = express();
const http = require('http');
const server = http.createServer(app);
const io = require('socket.io')(server);
app.set('socketio', io);
import { MongoClient } from 'mongodb';
var mongo_client;
var connection_string = 'mongodb://localhost:27017' // for local development environment
MongoClient.connect(connection_string, (err, client) => {
if (err) {
throw err;
} else {
mongo_client = client;
}
});
/*
lots of middleware and route handling
*/
server.listen(PORT, () => {
console.log(`running on port ${PORT}`);
});
In middleware, within the same file, I would make a call to the database with something like:
var collection = mongo_client.db('users').collection('users');
var query = { };
var options = { };
try {
var user = await collection.findOne(query, options);
return user;
} catch (err) {
console.log('error: ' + err);
}
I'm in the process of refactoring and have put the MongoClient.connect()
block in its own file.
config/database.js
now looks like:
const MongoClient = require('mongodb').MongoClient;
const { connection_string } = require('./environment_variables');
let mongo_client;
MongoClient.connect(connection_string, (err, client) => {
if (err) {
throw err;
} else {
mongo_client = client;
console.log('FIRST: mongo_client: ');
console.log(mongo_client);
}
});
console.log('SECOND: mongo_client: ');
console.log(mongo_client);
module.exports = mongo_client;
To begin testing how to import it into another file, I am doing this in app.js
:
const mongo_client = require('./config/database');
app.get('/', async (req, res) => {
const query = { username: 'my name' };
const collection = mongo_client.db('users').collection('users'); // <----- error is here, mongo_client is undefined
const result = await collection.findOne(query);
res.json({ result: result });
});
(note, when I better understand how all this works, I will move the route
and middleware
handling out of app.js
)
The console logs are:
SECOND: mongo_client:
undefined
FIRST: mongo_client:
MongoClient {
// ... lots of mongodb client properties here
}
UnhandledPromiseRejectionWarning: TypeError: Cannot read property 'db' of undefined
So it seems like MongoClient.connect()
is asynchronous and therefore mongo_client
is not defined before I try and call mongo_client.db('users').collection('users')
.
My questions are:
- How can this issue be resolved?
- Why wasn't it happening before I extracted
MongoClient.connect()
out ofapp.js
?
I've been reading articles like these:
MongoClient connection pooling
But they seem to either:
- Include subsequent calls to the database within the
MongoClient.connect()
function block (and i can't wrap my whole app within that function) OR - They call
app.listen()
within theMongoClient.connect()
function block (and this is also not feasible as I do several things withexpress()
andhttp
andsocket.io
)
Environment:
node v14.18.1
"mongodb": "^4.1.4" (native driver)
Edit:
I tried the following changes in config/database.js
per this article.
I replaced this:
const MongoClient = require('mongodb').MongoClient;
const { connection_string } = require('./environment_variables');
let mongo_client;
MongoClient.connect(connection_string, (err, client) => {
if (err) {
throw err;
} else {
mongo_client = client;
}
});
module.exports = mongo_client;
with this:
const MongoClient = require('mongodb').MongoClient;
const { connection_string } = require('./environment_variables');
const mongo_client = new MongoClient(connection_string);
const is_this_the_right_thing_to_do = async () => {
await mongo_client.connect();
};
is_this_the_right_thing_to_do();
module.exports = mongo_client;
The error went away and it is returning a result, but I am not sure if this is the correct way to do it.
Also, I am not sure how I can use the single instance of mongo_client
in all routes and middleware. I tried requiring it at the top of app.js
, hoping that the routes and middleware (in other files) that were required afterwards would have access to it, but they don't, this was the error in the route middleware:
UnhandledPromiseRejectionWarning: ReferenceError: mongo_client is not defined
Another edit:
I've posted an answer on someone else's question here which may be what I am looking for, will update this post after further testing.