Next.js MongoDB integration with API routes

January 12, 2021

Install the required mongo library

npm i -S mongodb

Setup connection details for local development environment

We assume that you already have a mongodb instance ready and running somewhere, meaning that you have a connection string, database and collection details at hand. This needs to be setup on your local dev environment e.g.: .env.local. We’ll setup them as follows

DATABASE_URL =
  "mongodb://rpi.fritz.box:27017/iphonePro?replicaSet=rs0&ssl=false"
MONGO_DB = "iphonePro"
MONGO_AD_COLLECTION = "ads"

Create a DB connection helper function

We will use this in the API routes later to connect to the database.

// https://raw.githubusercontent.com/vercel/next.js/canary/examples/with-mongodb/util/mongodb.js

import { MongoClient } from "mongodb"

const { DATABASE_URL, MONGO_DB } = process.env

if (!DATABASE_URL) {
  throw new Error(
    "Please define the DATABASE_URL environment variable inside .env.local"
  )
}

if (!MONGO_DB) {
  throw new Error(
    "Please define the MONGO_DB environment variable inside .env.local"
  )
}

/**
 * Global is used here to maintain a cached connection across hot reloads
 * in development. This prevents connections 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(DATABASE_URL, opts).then(client => {
      return {
        client,
        db: client.db(MONGO_DB),
      }
    })
  }
  cached.conn = await cached.promise
  return cached.conn
}

Create an API Route for a Mongo DB operation

In /pages/api/db create the DB operation function, e.g.: for insertion, as follows:

import { connectToDatabase } from "../../../helpers/db"

const insertOneToMongo = async (req, res) => {
  const { db } = await connectToDatabase()
  const { body } = req
  const { payload, collection } = body
  console.log(payload, collection)

  try {
    await db.collection(process.env[collection]).insertOne(payload)
    res.status(200).json({ result: "success" })
  } catch (e) {
    console.error(e)
    res.status(500).send(e)
  }
}

export default insertOneToMongo

Similarly for data retrieval from Mongo:

import { connectToDatabase } from "../../../helpers/db"

const getOneFromMongo = async (req, res) => {
  const { db } = await connectToDatabase()
  const { query } = req
  const { qry, collection } = query
  try {
    const result = await db
      .collection(process.env[collection])
      .findOne(JSON.parse(qry))
    res.status(200).json({ result })
  } catch (e) {
    console.error(e)
    res.status(500).send(e)
  }
}

export default getOneFromMongo

One more, for mongo upsert (insert in case record with a provided filter condition does not exist, otherwise update):

import { connectToDatabase } from "../../../helpers/db"

const replaceOneInMongo = async (req, res) => {
  const { db } = await connectToDatabase()
  const { body } = req
  const { filter, payload, upsert, collection } = body

  try {
    await db
      .collection(process.env[collection])
      .replaceOne(filter, payload, { upsert })
    res.status(200).json({ result: "success" })
  } catch (e) {
    console.error(e)
    res.status(500).send(e)
  }
}

export default replaceOneInMongo

Call these from your code where needed

How to call these functions in your code? See sample below (using the mongo upsert function we just created before)

const saveInternalProfile = () => {
  setshowInsertModal(true)
  setTimeout(async () => {
    try {
      const _profile = { ...profile }
      delete _profile._id
      await Axios.post("/api/db/replaceOneInMongo", {
        filter: { email: profile.email },
        payload: _profile,
        upsert: true,
        collection: "MONGO_INTERNAL_USERS_COLLECTION",
      })
      setshowInsertModal(false)
      toast.success("Profile mentése sikeres")
    } catch (error) {
      setshowInsertModal(false)
      toast.error("Profile mentése sikertelen")
    }
  }, 500)
}

Hope this helps. emoji-thumbsup