Reputation: 765
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
Reputation: 6603
The connection should be reused for the following reasons:
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
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