Connections are open with MongoDB

461 Views Asked by At

I am developing a project with NextJS and store my data with MongoDB and have issues with MongoDB connections.

The connection logic is simple:

  1. User connects to a page
  2. Page contacts my API to get page info
  3. My API using middleware gets data from MongoDB

According to the logic, I have only one user connects to mongoDB (system user). But the problem is I have a lot of connections open in MongoDB (see picture). It seems my middleware doesn't close the connection. Is this a problem with my code or it's MongoDB logic?

P.S. When I shut down my local project MongoDB connections count drops to 0.

Here is a sample of my middleware where the system connects MongoDB.

import { MongoClient } from 'mongodb';
import nextConnect from 'next-connect';

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

async function database(req, res, next) {
  if (!client.isConnected()) await client.connect();
  req.dbClient = client;
  req.db = client.db('test');
  return next();
}

const middleware = nextConnect();

middleware.use(database);

export default middleware;

According to my code, If we have an open connection - to use open connection. I took the code above from this mongodb tutorial

What should I do?

3

There are 3 best solutions below

1
Ivan V. On

The problem is that API routes are serverless meaning that they are created when needed and destroyed when the underlying serverless hosting structure deems appropriate. So you need to find a way not to create a connection to a database for every request.

/**
 * Global is used here to maintain a cached connection across hot reloads
 * in development. This prevents connections from growing exponentially
 * during API Route usage.
 */

let cached = global.mongo

if (!cached) {
  cached = global.mongo = { conn: null, promise: null }
}

export async function connectToDatabase() {
  if (cached.conn) {
    return cached.conn
  }

  if (!cached.promise) {
    const opts = {
      useNewUrlParser: true,
      useUnifiedTopology: true,
    }

    cached.promise = MongoClient.connect(MONGODB_URI, opts).then((client) => {
      return {
        client,
        db: client.db(MONGODB_DB),
      }
    })
  }
  cached.conn = await cached.promise
  return cached.conn
}

You can see the complete example in the Next.js examples repository

0
ChilTest On

Here I reworked my middleware to use the cached connections. (thanks to this answer)

  import { MongoClient } from 'mongodb';
  import nextConnect from 'next-connect';

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

  //with serverless we need to use cache to prevent re-opening connection
  let cached = global.mongo


  if (!cached) {
    cached = global.mongo = { conn: null, promise: null }
  }

  async function database(req, res, next) {
    if (!cached.promise) {
      cached.promise = mongoClient.connect().then((client) => {
        return {
          client,
          db: client.db(process.env.MONGODB_DB),
        }
      })
      cached.conn = await cached.promise
    }

    req.dbClient = cached.conn.client
    req.db = cached.conn.db

    return next();
  }

  const middleware = nextConnect();

  middleware.use(database);


  export default middleware;
0
Marc Gagnon On

given new changes on next-connect, this is how I made it worked based on previous code

import { MongoClient } from 'mongodb';
import nc from 'next-connect';

const MONGODB_URI = process.env.MONGODB_URI
const MONGODB_DB = process.env.MONGODB_DB

const mongoClient = new MongoClient(MONGODB_URI, {
    useNewUrlParser: true,
    useUnifiedTopology: true,
});

//with serverless we need to use cache to prevent re-opening connection
let cached = global.mongo


if (!cached) {
    cached = global.mongo = { conn: null, promise: null }
}

async function database(req, res, next) {
    if (!cached.promise) {
        cached.promise = mongoClient.connect().then((client) => {
            return {
                client,
                db: client.db(MONGODB_DB),
            }
        })
        cached.conn = await cached.promise
    }

    req.dbClient = cached.conn.client
    req.db = cached.conn.db

    return next();
}

const middleware = nc();

middleware.use(database);


export default middleware;

and my .env file :

MONGODB_URI=mongodb://localhost:27017/
MONGODB_DB=hotelcrud