How to use MongoDB with Mongoose inside Next JS

How to use MongoDB with Mongoose inside Next JS

Post by: Niraj Dhungana

Posted on: Nov 14, 2021

#Next JS# MongoDB# Mongoose

“Innovation is taking two things that exist and putting them together in a new way.“

Tom Freston (born 1945), Co-founder of MTV

In my opinion, React JS is one of the great boons that happened to programmers. Then the Vercel team combined React JS and their other magic and invented a new thing called Next JS.

Why Next JS and MongoDB

At first I wasn't convinced to use Next JS, but later I needed a way to build a website with SEO.

The main plan was to build a blog website and the condition was, I didn't want to use WordPress and also I wanted to use the knowledge I already have.

So, the technology stack I wanted to use to build my website was MongoDB, Node JS, and React and the most important thing with SEO. Then you can guess the rest.

There are lots of resources available to learn how we can use Next JS with different tech stacks. But the main problem is that those resources are not beginner friendly.

So, I decided to share some of the experience that I gained while developing my Next JS website with MongoDB or to be more specific with mongoose.

Connect MongoDB with Next JS

Here, the code that I am using to explain how to connect MongoDB and Next JS using mongoose is not written by me. This is the code that I copied from the official Next JS github repo.

1
2
3
4
//  lib/dbConnect.js

import mongoose from "mongoose";
const MONGODB_URI = process.env.MONGO_DB_URL;

First create a file called dbConnect inside a folder called lib or you can choose their name according to your convenience. Now you can import mongoose from mongoose and store the MongoDb connection URL inside a variable called MONGO_DB_URI.

Inside Next JS you don’t need to install dotenv or any other dependencies to use process.env but don’t forget to install mongoose. Read more about next js environment variables.

1
if (!MONGODB_URI) {

throw new Error( "Please define the MONGODB_URI environment variable inside .env.local" ); }

Then there is no need for an explanation for the code above but while I was checking for this if condition I got a bug on production and I removed it so be careful about it.

1
2
3
let cached = global.mongoose;

if (!cached) {

cached = global.mongoose = { conn: null, promise: null }; }

Ok here is the code which might feel a little bit confusing for beginners, the code from inside the if block.

Let me explain what is happening here. The first line is clear on its own but you may think from where the hell this global comes from.

Then it's like window obj inside a browser but for only node js environments and inside the same module only. You can read more about it here.

After that we are checking if there is no cached object or the connection is making for the first time then we are resetting the value of cached.

Inside the if block first we are assigning cached = global.mongoose or if global.mongoose is not there then assign the object with conn null and promise null. But this condition will run only if there is no global.mongoose right.

So I go with this code.

1
2
3
let cached = global.mongoose;

if (!cached) {

cached = { conn: null, promise: null }; }

Then finally here we have the actual code.

1
async function dbConnect() {

if (cached.conn) { return cached.conn; }

if (!cached.promise) { const opts = { bufferCommands: false, };

1
    cached.promise = mongoose.connect(MONGODB_URI, opts).then((mongoose) => mongoose);

} cached.conn = await cached.promise; return cached.conn; }

Inside this function first we are checking if there is already a cached.conn, meaning the connection was already made so no need for a new connection. So, let’s return the old mongodb connection.

If that’s not the case then let’s check for the cached.promise, if there is any unresolved promise. If not then let’s create a new options (opts) object with the property bufferCommands and value false.

Also you can have other options inside this object but hey let's talk first what this bufferCommands is doing here.

According to mongoose official docs. By default, mongoose buffers commands when the connection goes down until the driver manages to reconnect. To disable buffering, set bufferCommands to false.

In a simple word we are talking to mongoose drivers, hey look we don’t want to do any buffering for old connections, don’t try to reconnect just make it false.

Then we have the actual code for the mongodb connection. And we are storing it inside cached.promise because if you notice we are not awaiting for the result of mongoose connection.

And outside of the if block we are awaiting for cached.promise and storing it inside cached.conn. If there was no cached.promise this would be the line that would have run.

At the end we are returning the actual connection and don’t forget to export this dbConnect from the file.

CONCLUSION: First we are checking if there is already a cached. If not then we are resetting it’s value, like you saw already {conn: null, promise: null}. Then inside the dbConnect method we are checking if there is already a connection then returning the old connection.

Connecting to mongodb is an asynchronous task so while making a connection first we are storing it inside a promise. If our app tries to connect to mongodb before the previous promise is resolved we will wait for the old promise to be resolved.

If not then we are making a new connection request inside the if(!cached.promise) block. So, with this implementation we are not making new connections every time.

Ok, now you can use your dbConnect method where ever you wnat to connect to mongodb from inside your nextj js app. That’s it for now or you can also read.

How to create and export mongoose models correctly inside Next JS