Next.js: functions timeout if mongodb not closed; functions stops working if mongodb closed

Hello, I have been following the official Next.js & Mongodb way of doing things (next.js/examples/with-mongodb/lib/mongodb.ts at canary · vercel/next.js · GitHub). It works well on other hosting providers, but does not on Netlify

  • If I use that method, whenever I call any of my endpoints that uses mongodb, the endpoint works but does not fully respond before the 10s timeout. After the timeout I receive my JSON payload and a status 200.
    This is problematic because the Mongodb answers in a few ms, and it’s really just Netlify that keeps the Response “alive” until timeout although it already sent the payload a few ms after receiving the call.
    My guess is that for some reason Netlify’s function is kept alive by the mongodb connection not closed

  • If I use that method and close the mongodb connection before returning Response.json(), the first api call works, and then any other api endpoint using that mongodb clientPromise fails “Need to have an active connection blablabla…” which is the error for MongoDb saying it is not connected.

My understanding was that every function call is containerized and does not share state between api endpoints… So I do not understand how closing the connection for one api call closes the connection for all my API endpoints. Is this way of doing sharing the connection between functions?

If yes, that’s OK, but then Netlify needs to be fixed so the api call returns when I return a response and not after timeout if it detects that there is still a mongodb connection opened.

the code I use :

// lib/providers/mongodb.js
import { MongoClient, ServerApiVersion } from "mongodb";

if (!process.env.MONGODB_URI) {
  throw new Error("Add Mongo URI to .env.local");
}

const uri = process.env.MONGODB_URI;

let client;
let clientPromise;

if (
  process.env.NODE_ENV === "development"
) {
  if (!global._mongoClientPromise) {
    client = new MongoClient(uri, options);
    global._mongoClientPromise = client.connect();
  }
  clientPromise = global._mongoClientPromise;
} else {
  client = new MongoClient(uri, options);
  clientPromise = client.connect();
}

export default clientPromise;

// /api/foo/route.js

import clientPromise from "@/lib/providers/mongodb";

export async function GET(request, context) {
  const id = context.params.id;
  
//  console.time('db');
  const client = await clientPromise;
  const db = client.db("my-db"); // Choose a name for your database
  let result = await db.collection("my-collection").findOne({ id: Number(id) });
//  console.timeEnd('db'); // db : 875 ms

 // if I do not close here, Netlify does not answer before the 10s timeout
 // but the answer contains the right payload
 // client.close();
 // if I close here, then subsequent api calls or any other api calls fail because mongodb is not connected

  return Response.json(result);
}


Fix: If I use a scheme where I create a new connection for each call, instead of using the official next.js/mongodb that shares “clientPromise”, Netlify answers fast and right.

But since it’s not the “official way” presented on Next.js or MongoDB or even on Netlify, and because I saw quite a few threads speaking about netlify timeout & mongodb and the answer was always “your mongodb is too slow”, I wanted to report the problem is not necessarily mongodb, but Netlify functions.

the code I use for it to work well on Netlify

// lib/providers/mongodb
import { MongoClient, ServerApiVersion } from "mongodb";

const uri = process.env.MONGODB_URI;

const options = {
  serverApi: {
    version: ServerApiVersion.v1,
    strict: true,
    deprecationErrors: true,
  },
};

export async function connect() {
  const client = new MongoClient(uri, options);
  return await client.connect();
}

export async function close(client) {
  await client.close();
}
// api/foo/route.js
import { connect, close } from "@/lib/providers/mongodb";

export async function GET(request, context) {
    const id = context.params.id;
  
  const client = await connect();
  const db = client.db("my-db"); // Choose a name for your database
  let result = await db.collection("my-collection").findOne({ id: Number(id) });

  await close(client);
 
  return Response.json(result);
}

This is currently a bug with Netlify Functions v2 and the fix is ready. However, another fix in the Next.js Runtime v5 is pending.

This is not related to the old threads that you’re referring to.

1 Like