Originally published at blog.usman-s.me.

Hey all! 👋

Next.js is an amazing full-stack framework and MongoDB is a great NoSQL database. Using them together will make an app super fast and awesome! In this post, we’ll go ahead and set up the Mongoose ODM inside our Next.js app to make use of MongoDB!

So let’s see how you can set up Mongoose in a Next.js app to connect and interact with your MongoDB database!

Setting up mongoose and the connection string

In your Next.js project, to set up mongoose you simply have to install it as a dependency just as you do with Node.js.

npm i mongoose

After installing mongoose, we’ll create a folder called utils in our root and create a new file named connectMongo.js file.

In this file, we will export a function that connects us to MongoDB.

image.png

import mongoose from 'mongoose';

const connectMongo = async () => mongoose.connect(process.env.MONGO_URI);

export default connectMongo;

Also create a file named .env.local in the root of your project to store the connection URI in your environment variable and hide it from the main code.

# I am using MongoDB locally but you can use MongoDB Atlas also if you want
MONGO_URI="mongodb://localhost:27017/mongoose_nextjs_demo"

Create a basic mongoose model

Now that mongoose is set up successfully in our Next.js project, the rest of the work is quite similar to a Node.js app. I personally like to create a folder called models in the root of my project and create my model files there, just like a normal node.js app.

image.png

So now we’ll create a file named testModel.js in our models folder where we’ll create our mongoose model.

import { Schema, model, models } from 'mongoose';

const testSchema = new Schema({
  name: String,
  email: {
    type: String,
    required: true,
    unique: true,
  },
});

const Test = models.Test || model('Test', testSchema);

export default Test;

IMPORTANT: Notice how we use models.Test and then the logical OR operator and then use the model function by mongoose. We do that because we don’t want to create a new model every single time we hit an API route in Next.js. If you don’t do that and just go with model('Test', testSchema), you might face an error that would prevent creating/updating etc.

Using mongoose in API routes

Now that we have our model created, we can use it to see it in action!

Next.js is a full-stack framework, and so it also provides a node environment where we can run Node.js back-end code easily and integrate that with the frontend.

In the pages/api folder, we can create a file or folder that will ultimately create an API route and we can write back-end code in that file and call it as a REST API.

image.png

For this demo, I created a folder test and a file add.js inside of it which gives the path /api/test/add.

import connectMongo from '../../../utils/connectMongo';
import Test from '../../../models/testModel';

/**
 * @param {import('next').NextApiRequest} req
 * @param {import('next').NextApiResponse} res
 */
export default async function addTest(req, res) {
  try {
    console.log('CONNECTING TO MONGO');
    await connectMongo();
    console.log('CONNECTED TO MONGO');

    console.log('CREATING DOCUMENT');
    const test = await Test.create(req.body);
    console.log('CREATED DOCUMENT');

    res.json({ test });
  } catch (error) {
    console.log(error);
    res.json({ error });
  }
}

Here, we import the connectMongo function and our Test model that we created from the respective files. And the big comment I have at the top is JSDoc which can be used to provide autocomplete and typing in the IDE. You can omit it if you want.

Finally, the code is simple and straightforward, you can use the normal mongoose style code to create a new document. By getting the data from req.body.

You can test it from the Thunder Client extension in VS Code, Postman or Insomnia. Whatever you wish! I like to use Thunder Client.

image.png

Create new document from front-end

Now that we have our back-end API created and we have verified that it’s working, we can quickly write some front-end code to make it usable in our app.

On the homepage inside the index.js file, I changed the file so that when we click the button, a new document will get added to the database.

import Head from 'next/head';
import Image from 'next/image';
import styles from '../styles/Home.module.css';

export default function Home() {
  const createTest = async () => {
    const randomNum = Math.floor(Math.random() * 1000);
    const res = await fetch('/api/test/add', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({
        name: `Test ${randomNum}`,
        email: `test${randomNum}@test.com`,
      }),
    });
    const data = await res.json();
    console.log(data);
  };
  return (
    <div className={styles.container}>
      <Head>
        <title>Create Next App</title>
        <meta name='description' content='Generated by create next app' />
        <link rel='icon' href='/favicon.ico' />
      </Head>

      <main className={styles.main}>
        <button onClick={createTest}>Create Test</button>
        <h1 className={styles.title}>
          Welcome to <a href='https://nextjs.org'>Next.js!</a>
        </h1>

        <p className={styles.description}>
          Get started by editing{' '}
          <code className={styles.code}>pages/index.js</code>
        </p>

        <div className={styles.grid}></div>
      </main>

      <footer className={styles.footer}>
        <a
          href='https://vercel.com?utm_source=create-next-app&utm_medium=default-template&utm_campaign=create-next-app'
          target='_blank'
          rel='noopener noreferrer'
        >
          Powered by{' '}
          <span className={styles.logo}>
            <Image src='/vercel.svg' alt='Vercel Logo' width={72} height={16} />
          </span>
        </a>
      </footer>
    </div>
  );
}

Fetch and display documents

Now it’s time to use Next.js’s best feature! Server-Side Rendering. We can use SSR in Next.js to easily run back-end Node.js code as we want and the data can be easily accessible through the props to the page.

In the index.js file itself, we’ll import the connectMongo and Test again and use them inside the getServerSideProps function that we have to export like this 👇

import connectMongo from '../utils/connectMongo';
import Test from '../models/testModel';

export const getServerSideProps = async () => {
  try {
    console.log('CONNECTING TO MONGO');
    await connectMongo();
    console.log('CONNECTED TO MONGO');

    console.log('FETCHING DOCUMENTS');
    const tests = await Test.find();
    console.log('FETCHED DOCUMENTS');

    return {
      props: {
        tests: JSON.parse(JSON.stringify(tests)),
      },
    };
  } catch (error) {
    console.log(error);
    return {
      notFound: true,
    };
  }
};

In this function, we can easily fetch any data we want and it will be done on the server and then we have to return it as props. That will be accessible to the page. You can read about getServerSideProps on the Next.js docs

IMPORTANT: Make sure to sanitize the tests variable with JSON.parse(JSON.stringify(tests)) because it contains ObjectID from MongoDB instead of a normal string. This trick converts it into a string that can be passed in the return object.

That’s it! After we’re done fetching the data we can easily display it by accessing it through the props on our page and we can use it however we want. In this case we’ll map over that data to output every document like so

export default function Home({ tests }) {
  // ...
  return (
    //   ...
    <div className={styles.grid}>
      {tests.map((test) => (
        <a
          href="https://nextjs.org/docs"
          key={test._id}
          className={styles.card}
        >
          <h2>{test.name} &rarr;</h2>
          <p>{test.email}</p>
        </a>
      ))}
    </div>
    // ...
  );
}

Finally, this is what our page looks like:

image.png

I have explained everything deeply in the YouTube tutorial below 👇👇

I hope this post helped you successfully set up Mongoose in your Next.js app. If it did, please leave a like!

Comment down your thoughts! There is always room for improvement so let me know your suggestions on this project!

Connect with me on my YouTube channel and my Twitter 😉

Thanks for reading ✌


Join discussions at our discord server >
Did you learn something new today? Spread the word!

Contribute to the Genics Blog!

Genics Blog is a purely open source publication. Authors at Genics post highly resourceful content on varied topics relevant to the developer community. Join us and start publishing today!

Contribute

Related content