user3343396
user3343396

Reputation: 765

next.js and mongodb atlas - getting "Connections % of configured limit has gone above 80" alert

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!

Upvotes: 8

Views: 2370

Answers (2)

Nikolai Kiselev
Nikolai Kiselev

Reputation: 6603

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.

Upvotes: 8

atomiks
atomiks

Reputation: 744

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;

Upvotes: 1

Related Questions