7

I saw a lot of posts and articles about this alert on MongoDB Atlas ("Connections % of configured limit has gone above 80"), but couldn't figure out how to solve it in my Next.js application.

I create my db connection outside the handler function. I used a middleware withDatabase.js:

const client = new MongoClient(process.env.MONGODB_URI, { 
    useNewUrlParser: true, 
    useUnifiedTopology: true 
});

const addDbToRequest = (handler, req, res) => {
    req.db = req.connection.db("MYDBNAME");
    return handler(req, res);
};

const withDatabase = handler => async (req, res) => {
    if (!client.isConnected()) {
        await client.connect();
    }
    req.connection = client;
    return addDbToRequest(handler, req, res);
};

export default withDatabase;

This middleware wraps the API endpoint handler.

Now, if I close the connection on every API handler when it finishes, like this:

    const { connection } = req;
    if (connection) {
        connection.close();
    }

Than, I'm getting an error on the second request to the same api handler:

MongoError: Topology is closed, please connect

And if i'm not closing the connection, i'm getting this alert (after a short time of use) to my email:

Connections % of configured limit has gone above 80

What is the best practices to work with MongoDB Atlas in a Next.js application?

Thanks!

user3343396
  • 725
  • 2
  • 13
  • 25
  • hi did you find any working solution? As the accepted answer is not working for my exact same case – Agent K Aug 12 '23 at 14:16

2 Answers2

8

The connection should be reused for the following reasons:

  1. Opening and closing DB connections on every API request is slow.
  2. It's hardly scalable. Assuming you're making a few API requests simultaneously per user, you will reach the same connections limit quickly when the app gets more users.

How do I manage MongoDB connections in a Node.js web application?

2022 update:

import { MongoClient } from 'mongodb'

const uri = process.env.MONGODB_URI
const options = {}

let client
let clientPromise

if (!process.env.MONGODB_URI) {
  throw new Error('Please add your Mongo URI to .env.local')
}

if (process.env.NODE_ENV === 'development') {
  // In development mode, use a global variable so that the value
  // is preserved across module reloads caused by HMR (Hot Module Replacement).
  if (!global._mongoClientPromise) {
    client = new MongoClient(uri, options)
    global._mongoClientPromise = client.connect()
  }
  clientPromise = global._mongoClientPromise
} else {
  // In production mode, it's best to not use a global variable.
  client = new MongoClient(uri, options)
  clientPromise = client.connect()
}

// Export a module-scoped MongoClient promise. By doing this in a
// separate module, the client can be shared across functions.
export default clientPromise

Before 2022

Default MongoClient configuration has maximum number of connections per pool (poolSize) set to 5. So, you shouldn't see more than ~5 connections in MongoDB Atlas if you have only one app instance running and checking whether a client is connected already, like you do.

if (!client.isConnected()) {
  await client.connect();
}

Note, that Next.js "restarts" on every request in the development mode (next dev) and it seems it affects MongoClient cache and creates many connections. However in production mode, you shouldn't experience this issue.

Nikolai Kiselev
  • 6,201
  • 2
  • 27
  • 37
  • so the problem is the develop mode, because i'm not closing the connection.. – user3343396 May 01 '20 at 08:59
  • 1
    what about 'Fast Refresh'? Then in develop mode every code change will trigger new connections which will hit the MongoDB Atlas limit quickly – Se7enDays Sep 10 '20 at 11:51
  • Until I learned of the poolSize configuration this did not make any sense. As the MongoDb Guide states `poolSize: Specifies the maximum size of the instance connection pool.` So when you execute `client.connect()` this is setting up a connection 'pool' (which can consist of multiple connections). The developers probably should have worded the `connect` function differently (maybe 'ICP' for instance connection pool) so users are not under the impression that it is simply setting up a single connection to the database. MongoDb Atlas does not seem to track connection pools, only connections. – w. Patrick Gale Jul 08 '21 at 04:36
  • Could someone provide an example of how to do this with on NextJS with mongoose? – josealvarez97 Jul 28 '23 at 15:59
  • @josealvarez97 did you find any working solution? I am desperately looking for the same. – Agent K Aug 12 '23 at 14:18
0

To fix the max connections reached issue in Next's dev mode, I found writing the reused/cached client object to the global Node object allowed it to persist between refreshes.

const MongoClient = require('mongodb').MongoClient;

async function useMongoClient() {
  if (!this.client) {
    this.client = await MongoClient.connect(process.env.MONGODB_URI, {
      useUnifiedTopology: true,
    });
  }

  return this.client;
}

module.exports = useMongoClient;
atomiks
  • 684
  • 6
  • 11
  • in my env "this" comes up undefined and i cannot attach any params to it... "global" is a thing, but i tried to create "global.cachedDB" and it always comes up undefined. – K.H. B Apr 16 '21 at 13:57