NextJs mongodb: App connects too many times to the database

I have a NextJs app built with mongodb database.
I was surprised to see that I quickly almost reached the threshhold of maximum connections:


This app used to be built with node and react and this was never the case.
But the logic of connecting to the database has changed.
I have this function that connects to the database:

// lib/mongodb.ts
import mongoose from "mongoose";

// #TODO
// I tried putting this in types/global.d.ts
// But, I kept getting the mongoose global error
// So I brought it here
// Not sure if it makes sense to keep this here
declare global {
  var mongoose: {
    conn: any;
    promise: Promise<any> | null;
  };
}

const MONGODB_URI = process.env.MONGODB_URI;

if (!MONGODB_URI) {
  throw new Error("Please define the MONGODB_URI environment variable");
}

let cached = global.mongoose;

if (!cached) {
  // conn: The established connection
  // promise: Ongoing connection attempt
  /**
     * cached.promise: Prevents multiple concurrent connection attempts during the first request when the connection is not yet established.
       cached.conn: Efficiently returns the active connection after it has been established.
     */
  cached = global.mongoose = { conn: null, promise: null };
}

async function connectToDatabase() {
  /**
   * By storing the established connection in cached.conn, subsequent requests can
   * skip the connection process below entirely, leading to faster response times.
   */
  if (cached.conn) {
    return cached.conn;
  }

  /**
   * By checking cached.promise, we prevent redundant connection attempts.
   * If a connection attempt (promise) has already been made, we don't need to initiate
   * another one, we can reuse it, thus avoiding unnecessary load on the database.
   */
  if (!cached.promise) {
    /**
     * The connection promise is stored in cached.promise so that any subsequent requests made
     * before the connection resolves can await the same promise, preventing redundant connection
     *  attempts.
     */
    cached.promise = mongoose
      .connect(MONGODB_URI as string)
      .then((mongoose) => {
        return mongoose;
      })
      .catch((error) => {
        console.error("🚀 ~ Error connecting to MongoDB:", error);
        throw error; // Re-throw the error after logging
      });
  }

  try {
    cached.conn = await cached.promise;
  } catch (error) {
    throw error;
  }

  return cached.conn;
}

export default connectToDatabase;

Where are you running this? Vercel? This could be related to the serverless deployment model of your hosting set up… also - what does your connection string look like? (don’t share your password, obviously - but do you have maxPoolSize set? i.e. MONGODB_URI=mongodb+srv://<user>:<pass>@cluster.mongodb.net/myDB?retryWrites=true&w=majority&maxPoolSize=10

Hello,
This is indeed deployed on Vercel. And I just found this relevant discussion on Github.

My connection url looks like this:
MONGODB_URI=mongodb+srv://username:pwd@cluster0.mchmj.mongodb.net/db_name?retryWrites=true&w=majority&appName=Cluster0

Pretty sure you can use globalThis to alleviate these problems… check out this repo I just whipped up to test… https://github.com/mrlynn/globalThisExampleNext

The secret sauce:

if (process.env.NODE_ENV === 'development') {
  if (!globalThis._mongoClientPromise) {
    console.log(`🌱 [${clientId}] Creating NEW MongoClient instance (dev)`);
    client = new MongoClient(uri, options);
    globalThis._mongoClientPromise = client.connect();
    globalThis._mongoClientId = clientId;
  } else {
    console.log(`♻️ Reusing MongoClient [${globalThis._mongoClientId}] (dev)`);
  }
  clientPromise = globalThis._mongoClientPromise;
} else {
  // Production/serverless
  console.log(`🚀 [${clientId}] Creating MongoClient instance (prod/serverless)`);
  client = new MongoClient(uri, options);
  clientPromise = client.connect();
}

I think this should work… try it and let me know.

Regards,
Mike

Hello,
Thank you for your reply.
The thing is I’m using mongoose in other places in the app, notably when defining models. So I’m not sure if I should replace the mongoose driver with the native mongodb driver.

Or maybe I should use your same approach of using globalThis but with mongoose?
I’ll be trying this as I wait for your response :slight_smile:

Check this out: Mongoose v8.13.0: Using Mongoose With AWS Lambda
I assume you’re using Vercel’s serverless functions, which are actually AWS Lambda functions. This method should help.