1

I'm trying to reuse my MongoClient connection in another module and the export is coming up as null upon import.

index.js

import app from './server.js'
import pkg from 'mongodb'
const { MongoClient } = pkg

let db = null

async function listDatabases(client) {
  try {
    const dbList = await client.db().admin().listDatabases()
    console.log('Databases:')
    dbList.databases.forEach((db) => {
    console.log(` - ${db.name}`) // I see the databases here
 })
} catch (error) {
   console.error(error)
 }
}

MongoClient.connect(uri, {
  useNewUrlParser: true,
  poolSize: 50,
  useUnifiedTopology: true,
})
 .then(async (client) => {
   listDatabases(client) // I see the databases here
   db = client
   app.listen(port, () => {
    console.log(`listening on port ${port}`)
  })
 })
.catch((err) => {
  console.error(err.stack)
  process.exit(1)
})

export default db

and then in OrdersDAO.js, I'm importing db to be able to access the db and collection. I referred to this StackOverflow

import db from '../index.js'

static async getOrders() {
   try {
   // db is null here
   console.log('OrdersDAO.getOrders ~~~ made it to the OrdersDAO step 2', db)

  } catch (e) {
    console.error(`OrdersDAO ~~~ Unable to get orders: ${e}`)
    return { e }
  }
}

What am I doing wrong?

Below was my original async function and subsequent function call: index.js

const client = new MongoClient(uri, {
  useNewUrlParser: true,
  poolSize: 50,
  useUnifiedTopology: true,
})

async function connectToMongoDB() {
  try {
    await listDatabases(client)
    app.listen(port, () => {
    console.log(`listening on port ${port}`)
   })
 } catch (e) {
    console.error(`Index.js ~~~ Unable to get database.collection: ${e}`)
 } finally {
    console.log('Mongo Connection is closing ~~~~')
    await client.close()
  }
}

connectToMongoDB().catch(console.error)
Kevin T.
  • 668
  • 3
  • 12
  • 29

1 Answers1

0

Connecting to a database is an asynchronous operation, and as such, it returns a Promise. anything assigned inside that function will only be available after that promise resolves. However you are returning the db object outside the promise, so the js engine does not wait and it is returning the initial value, which is null.
Here is a neat and clean solution I implemented in a recent project, that is also taking into account connection timeout:

db.js:

const {MongoClient}  = require("mongodb");
const {DATABASE_URL} = require("./config");

async function connect() {
  const client = await MongoClient.connect(DATABASE_URL, {
    useNewUrlParser: true
  });
   const db =  client.db();
   return { client, db };
 };

module.exports = { connect, dbHelper: connect()}

crud.js:

const { dbHelper, connect } = require("./db");

async function reconnect() {
    return connect();
};

async function getConnectionOrReconnect() {
    let mongo = await Promise.resolve(dbHelper);
    if (!mongo.client.isConnected()) mongo = await reconnect();
    return mongo;
}

//other api functions here

Here, I am exporting both the connect function itself and the connection result (as a promise). Than in the other file I'm resolving the imported connection and if it has timed-out I am reconnecting again.

ISAE
  • 519
  • 4
  • 12
  • But with connection pooling, I shouldn't have to reconnect everytime I need the client.? – Kevin T. Jul 06 '21 at 17:45
  • That was just an added benefit (I copy-pasted it from my project where I didn't use pooling for some reason). The main point is the way db object is exported as a promise from inside the connect function in the db.js file, and than resolved in the other file. – ISAE Jul 06 '21 at 18:21