“Topology was destroyed” when using MongoDB with native driver and Express.js

This is because the code contains an anti-pattern: every time a new request comes in, it opens a new database connection, then closes that connection once the response was sent. It then subsequently tried to reuse the closed connection, hence the error message you’re seeing on the 2nd request.

What you want is to connect only once to the database for the lifetime of the application using a global connection object, then use that global object to perform your database operations.

Using this global object allows the MongoDB driver to properly create a connection pool to the database. This pool is managed by the MongoDB driver, and avoids the expensive connect/reconnect pattern.

For example:

// listen on this port
const port = 3000

// global database client object
var client = null

// listen on the configured port once database connection is established
MongoClient.connect('mongodb://localhost:27017', { useNewUrlParser: true }, (err, res) => {
  assert.equal(null, err)
  client = res
  app.listen(port, () => console.log(`Example app listening on port ${port}!`))
})

// use the client global object for database operations
app.get('/', (req, res) => {
  db = req.query.db
  col = req.query.col
  client.db(db).collection(col).find({}).toArray((err, docs) => {
    assert.equal(null, err)
    res.send(JSON.stringify(docs))
  })
})

Edit to answer your question in the comment:

Why does it tries to reuse previous connection when I do connect every time?

This is because in the original code, dbClient was globally defined. When dbClient.close() was called, the global dbClient was closed. An error was then produced when that dbClient object was reused. This is because connect() creates a connection pool instead of a single connection, and was not expected to be called multiple times per invocation.

If you move the dbClient variable from the global scope into the app.get() context, you’ll find that no error will be produced when you call the HTTP endpoint multiple times, as a new dbClient object was created every time.

Having said that, although it will work, this is not a recommended pattern. It’s better to use a pattern similar to the example code I posted above.

Leave a Comment