Next.js Functions

 

Using NEXT.js with MongoDB: Building Database-driven Applications

In today’s web development landscape, building dynamic and data-driven applications has become the norm. To achieve this, developers often rely on frameworks that allow seamless integration with databases. One such powerful combination is using NEXT.js, a popular React-based framework for server-side rendering, with MongoDB, a versatile NoSQL database. In this blog, we will explore how to create database-driven applications using NEXT.js and MongoDB, enabling you to build performant and scalable web applications.

Using NEXT.js with MongoDB: Building Database-driven Applications

1. Prerequisites

Before diving into the implementation details, it’s essential to have some prerequisites in place. Ensure you have the following installed on your machine:

  1. Node.js and npm: As NEXT.js is a JavaScript framework, you’ll need Node.js and npm (Node Package Manager) to manage dependencies and run the development server.
  2. MongoDB: Install and set up MongoDB on your local machine or use a cloud-based MongoDB service like MongoDB Atlas.
  3. A Code Editor: Choose a code editor of your preference (e.g., Visual Studio Code, Sublime Text, or Atom).

Now that you have the prerequisites set up, let’s get started with building database-driven applications using NEXT.js and MongoDB.

2. Setting Up a NEXT.js Project

To create a new NEXT.js project, open your terminal and run the following commands:

bash
# Create a new NEXT.js project
npx create-next-app my-mongodb-app
cd my-mongodb-app

This will set up a new NEXT.js project in a directory named “my-mongodb-app.” Now, let’s install the required dependencies for connecting to MongoDB.

3. Installing Dependencies

To interact with MongoDB from our NEXT.js application, we need to install the necessary packages. The primary packages we’ll use are:

  1. mongoose: This package allows us to define data models, connect to the MongoDB database, and perform CRUD operations easily.
  2. dotenv: This package will help us manage environment variables and store sensitive data, such as database connection strings.

Install these packages by running the following command in your terminal:

bash
npm install mongoose dotenv

With the required dependencies installed, we can proceed to set up the MongoDB connection.

4. Connecting to MongoDB

Before connecting to MongoDB, we need to obtain a connection string. If you’re using a local MongoDB instance, the connection string will typically be something like mongodb://localhost:27017/my-database. For MongoDB Atlas or other cloud-based services, follow their documentation to get the connection string.

To keep our connection string secure, we’ll use environment variables provided by the dotenv package. Create a new file in the root of your project called .env and add the following line, replacing <your-connection-string> with your actual MongoDB connection string:

c
MONGODB_URI=<your-connection-string>

Next, open the next.config.js file (create one if it doesn’t exist) in the root of your project and add the following code:

javascript
// next.config.js
module.exports = {
  env: {
    MONGODB_URI: process.env.MONGODB_URI,
  },
};

Now, we can connect to the MongoDB database in our application. Create a new file called db.js in the utils directory and add the following code:

javascript
// utils/db.js
import mongoose from 'mongoose';

const connectDB = async () => {
  try {
    await mongoose.connect(process.env.MONGODB_URI, {
      useNewUrlParser: true,
      useUnifiedTopology: true,
    });
    console.log('Connected to MongoDB');
  } catch (error) {
    console.error('Error connecting to MongoDB:', error.message);
    process.exit(1);
  }
};

export default connectDB;

We are using mongoose.connect to connect to the MongoDB database using the provided connection string from our environment variables. The useNewUrlParser and useUnifiedTopology options are recommended to avoid deprecation warnings.

Now, we can call the connectDB function from our application’s entry point to establish the MongoDB connection. Open the pages/api/index.js file and add the following:

javascript
// pages/api/index.js
import connectDB from '../../utils/db';

// Call the connectDB function
connectDB();

// Default API route
export default (req, res) => {
  res.status(200).json({ message: 'Hello from NEXT.js API!' });
};

With this setup, our NEXT.js application is now connected to the MongoDB database, and we can start building database-driven features.

5. Creating a Model and Performing CRUD Operations

To interact with data in MongoDB, we need to create a data model. In this example, let’s create a simple model for managing blog posts. Each blog post will have a title, content, and a timestamp for when it was created.

In the models directory, create a new file named Post.js with the following code:

javascript
// models/Post.js
import mongoose from 'mongoose';

const postSchema = new mongoose.Schema({
  title: { type: String, required: true },
  content: { type: String, required: true },
  createdAt: { type: Date, default: Date.now },
});

const Post = mongoose.model('Post', postSchema);

export default Post;

In this code, we define a Mongoose schema with the fields title, content, and createdAt. The createdAt field will automatically be set to the current date and time when a new blog post is created.

Now, we can use the Post model to perform CRUD operations on our MongoDB database.

6. Creating a New Blog Post

javascript
// pages/api/posts.js
import connectDB from '../../utils/db';
import Post from '../../models/Post';

connectDB();

export default async (req, res) => {
  if (req.method === 'POST') {
    const { title, content } = req.body;

    try {
      const post = await Post.create({ title, content });
      res.status(201).json({ post });
    } catch (error) {
      res.status(500).json({ error: 'Error creating blog post' });
    }
  }
};

In this code, we import the Post model and create a new API route to handle POST requests for creating blog posts. When a POST request is received, we extract the title and content from the request body and use the Post.create method to create a new blog post. If successful, the newly created post will be returned in the response.

7. Getting All Blog Posts

javascript
// pages/api/posts.js
// ...

export default async (req, res) => {
  if (req.method === 'GET') {
    try {
      const posts = await Post.find();
      res.status(200).json({ posts });
    } catch (error) {
      res.status(500).json({ error: 'Error retrieving blog posts' });
    }
  }
};

In this code snippet, we add another API route to handle GET requests for fetching all blog posts from the database. We use the Post.find() method to retrieve all documents from the Post collection and return them in the

8. Updating a Blog Post

javascript
// pages/api/posts/[id].js
import connectDB from '../../../utils/db';
import Post from '../../../models/Post';

connectDB();

export default async (req, res) => {
  const { id } = req.query;

  if (req.method === 'PUT') {
    const { title, content } = req.body;

    try {
      const post = await Post.findByIdAndUpdate(
        id,
        { title, content },
        { new: true }
      );

      if (!post) {
        return res.status(404).json({ error: 'Blog post not found' });
      }

      res.status(200).json({ post });
    } catch (error) {
      res.status(500).json({ error: 'Error updating blog post' });
    }
  }
};

In this code, we create an API route with a dynamic segment ([id]) to handle PUT requests for updating a blog post. We extract the id from the request query parameters and the updated title and content from the request body. We then use the Post.findByIdAndUpdate method to find the blog post with the given id and update its fields with the new values. The { new: true } option ensures that the updated document is returned in the response.

9. Deleting a Blog Post

javascript
// pages/api/posts/[id].js
// ...

export default async (req, res) => {
  const { id } = req.query;

  if (req.method === 'DELETE') {
    try {
      const post = await Post.findByIdAndDelete(id);

      if (!post) {
        return res.status(404).json({ error: 'Blog post not found' });
      }

      res.status(200).json({ message: 'Blog post deleted successfully' });
    } catch (error) {
      res.status(500).json({ error: 'Error deleting blog post' });
    }
  }
};

In this code snippet, we add another API route to handle DELETE requests for deleting a blog post. Similar to the update route, we extract the id from the request query parameters. We use the Post.findByIdAndDelete method to find the blog post with the given id and remove it from the database. If the post is successfully deleted, we return a success message in the response.

10. Fetching a Single Blog Post

javascript
// pages/api/posts/[id].js
// ...

export default async (req, res) => {
  const { id } = req.query;

  if (req.method === 'GET') {
    try {
      const post = await Post.findById(id);

      if (!post) {
        return res.status(404).json({ error: 'Blog post not found' });
      }

      res.status(200).json({ post });
    } catch (error) {
      res.status(500).json({ error: 'Error retrieving blog post' });
    }
  }
};

Here, we add one more API route to handle GET requests for fetching a single blog post. We use the Post.findById method to find the blog post with the given id and return it in the response. If the post is not found, we return a 404 error.

11. Building the Frontend

With the API routes set up to handle database interactions, let’s build the frontend components using NEXT.js. Create a new file called BlogPost.js in the components directory with the following code:

javascript
// components/BlogPost.js
import React from 'react';

const BlogPost = ({ post }) => {
  return (
    <div>
      <h2>{post.title}</h2>
      <p>{post.content}</p>
      <small>Created on: {new Date(post.createdAt).toLocaleString()}</small>
    </div>
  );
};

export default BlogPost;

In this code, we create a functional component BlogPost that takes a post object as a prop and displays the blog post’s title, content, and creation date.

Now, let’s create a page to display all blog posts and a page to view a single blog post. In the pages/posts/index.js file, add the following code:

javascript
// pages/posts/index.js
import React from 'react';
import BlogPost from '../../components/BlogPost';

const AllPosts = ({ posts }) => {
  return (
    <div>
      <h1>All Blog Posts</h1>
      {posts.map((post) => (
        <BlogPost key={post._id} post={post} />
      ))}
    </div>
  );
};

export async function getServerSideProps() {
  const res = await fetch('http://localhost:3000/api/posts');
  const { posts } = await res.json();

  return {
    props: {
      posts,
    },
  };
}

export default AllPosts;

In this code, we create a page component AllPosts that fetches all blog posts from our backend API using getServerSideProps. The posts data is then passed to the BlogPost component, which renders each blog post.

Next, create a new file named [id].js in the pages/posts directory with the following code:

javascript
// pages/posts/[id].js
import React from 'react';
import BlogPost from '../../components/BlogPost';

const SinglePost = ({ post }) => {
  return (
    <div>
      <BlogPost post={post} />
    </div>
  );
};

export async function getServerSideProps({ params }) {
  const { id } = params;

  const res = await fetch(`http://localhost:3000/api/posts/${id}`);
  const { post } = await res.json();

  return {
    props: {
      post,
    },
  };
}

export default SinglePost;

In this code, we create another page component SinglePost that fetches a single blog post based on the id from the dynamic route using getServerSideProps. The fetched post is then passed to the BlogPost component for rendering.

Conclusion

In this blog post, we explored how to build powerful database-driven applications using NEXT.js and MongoDB. We started by setting up a new NEXT.js project, connecting it to a MongoDB database, and creating a Mongoose model for blog posts. We then implemented API routes to handle CRUD operations for managing blog posts. Finally, we built the frontend components and pages to display all blog posts and view a single post.

This is just the beginning of what you can achieve with NEXT.js and MongoDB. You can extend this application with user authentication, comment functionality, and more. With the ability to efficiently handle large amounts of data, NEXT.js and MongoDB provide a robust foundation for creating modern and scalable web applications.

Previously at
Flag Argentina
Brazil
time icon
GMT-3
Accomplished Senior Software Engineer with Next.js expertise. 8 years of total experience. Proficient in React, Python, Node.js, MySQL, React Hooks, and more.