How to Build a Full-Stack Application Using Next.js and MongoDB
Building a full-stack application involves combining the front-end and back-end technologies so that they communicate seamlessly with each other. This article will guide you through creating a modern full-stack application using Next.js for the front end and MongoDB as the database. Next.js is a React-based framework that provides server-side rendering, API routes, and other features out of the box, while MongoDB is a NoSQL database that stores data in JSON-like documents.
By the end of this tutorial, you will have a working knowledge of how to:
Set up a Next.js project.
Connect to a MongoDB database.
Create API routes in Next.js to handle CRUD operations.
Build a responsive front-end that interacts with the database.
Let’s dive in!
Prerequisites
Before starting, make sure you have the following installed:
Node.js (v14+)
MongoDB (you can use MongoDB Atlas for cloud hosting or run a local instance)
Basic knowledge of React.js
Step 1: Setting Up the Next.js Project
Create a new Next.js app:
In your terminal, run the following command to create a new Next.js project:
bashCopy codenpx create-next-app fullstack-nextjs-mongodb cd fullstack-nextjs-mongodb
This will scaffold a basic Next.js project. Inside the
fullstack-nextjs-mongodb
directory, you’ll find the basic structure of the application.Install Dependencies:
For this project, we’ll need additional packages to interact with MongoDB and manage the API requests.
bashCopy codenpm install mongoose
mongoose
is an Object Data Modeling (ODM) library for MongoDB that helps manage database schemas and operations in a clean, organized way.
Step 2: Connecting to MongoDB
We’ll now set up the connection between Next.js and MongoDB using Mongoose.
Create a
.env.local
file to store environment variables:In the root directory, create a
.env.local
File and add your MongoDB connection string. If you're using the cloud version, you can get this string from MongoDB Atlas or provide a local MongoDB URI.bashCopy codeMONGODB_URI=mongodb+srv://<username>:<password>@cluster0.mongodb.net/myDatabase?retryWrites=true&w=majority
Create the MongoDB connection logic:
Inside the
lib
folder (create it if it doesn’t exist), create a file namedmongodb.js
to manage the database connection.javascriptCopy codeimport mongoose from 'mongoose'; const MONGODB_URI = process.env.MONGODB_URI; if (!MONGODB_URI) { throw new Error('Please define the MONGODB_URI environment variable inside .env.local'); } let cached = global.mongoose; if (!cached) { cached = global.mongoose = { conn: null, promise: null }; } async function connectToDatabase() { if (cached.conn) { return cached.conn; } if (!cached.promise) { const opts = { useNewUrlParser: true, useUnifiedTopology: true, }; cached.promise = mongoose.connect(MONGODB_URI, opts).then((mongoose) => { return mongoose; }); } cached.conn = await cached.promise; return cached.conn; } export default connectToDatabase;
This code ensures that your app maintains a single connection to the database across multiple API requests, reducing overhead.
Step 3: Creating a MongoDB Schema
Next, we’ll define a schema for the data stored in MongoDB. Let’s assume we are building a simple blog application with articles.
Inside the
models
directory (create it if it doesn’t exist), create a file namedArticle.js
:javascriptCopy codeimport mongoose from 'mongoose'; const ArticleSchema = new mongoose.Schema({ title: { type: String, required: [true, 'Please provide a title for the article.'], }, content: { type: String, required: [true, 'Please provide content for the article.'], }, date: { type: Date, default: Date.now, }, }); export default mongoose.models.Article || mongoose.model('Article', ArticleSchema);
This schema defines the structure of our articles, specifying that each article should have a title, content, and date.
Step 4: Setting Up API Routes
Next.js has a built-in API route system that allows us to createback-endd logic easily. Let’s create routes to handle CRUD operations (Create, Read, Update, Delete) for the articles.
Create an
api
directory under thepages
folder, and inside it, create a new filearticles.js
:javascriptCopy codeimport connectToDatabase from '../../lib/mongodb'; import Article from '../../models/Article'; export default async function handler(req, res) { const { method } = req; await connectToDatabase(); switch (method) { case 'GET': try { const articles = await Article.find({}); res.status(200).json({ success: true, data: articles }); } catch (error) { res.status(400).json({ success: false }); } break; case 'POST': try { const article = await Article.create(req.body); res.status(201).json({ success: true, data: article }); } catch (error) { res.status(400).json({ success: false }); } break; default: res.status(400).json({ success: false }); break; } }
This API route will handle GET requests to retrieve articles and POST requests to create new articles.
Step 5: Building the Front-end
Now, let’s create a simple front-end to interact with this API. We will build a page that displays all articles and a form to create new ones.
In the
pages
directory, modify theindex.js
file:javascriptCopy codeimport { useState, useEffect } from 'react'; export default function Home() { const [articles, setArticles] = useState([]); const [title, setTitle] = useState(''); const [content, setContent] = useState(''); useEffect(() => { fetch('/api/articles') .then((res) => res.json()) .then((data) => setArticles(data.data)); }, []); const handleSubmit = async (e) => { e.preventDefault(); const res = await fetch('/api/articles', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ title, content }), }); const newArticle = await res.json(); setArticles([...articles, newArticle.data]); setTitle(''); setContent(''); }; return ( <div> <h1>Articles</h1> <form onSubmit={handleSubmit}> <input type="text" value={title} onChange={(e) => setTitle(e.target.value)} placeholder="Title" /> <textarea value={content} onChange={(e) => setContent(e.target.value)} placeholder="Content" ></textarea> <button type="submit">Submit</button> </form> <ul> {articles.map((article) => ( <li key={article._id}> <h2>{article.title}</h2> <p>{article.content}</p> </li> ))} </ul> </div> ); }
This component fetches articles from tback-endend, displays them, and provides a form to create new ones.
Step 6: Running the Application
To run the application, use the following command:
bashCopy codenpm run dev
Navigate to http://localhost:3000
In your browser, you should see a list of articles and a form to create new articles.
Final Thoughts
You’ve now built a simple full-stack application using Next.js and MongoDB! This setup can be extended with features like user authentication and pagination or even deployed to platforms like Vercel or Heroku.
Next.js makes it incredibly easy to build dynamic web applications with server-side rendering, while MongoDB provides flexibility in managing data. As you continue to work on more advanced applications, you can explore additional Next.js features like static generation, API optimizations, and MongoDB integrations with services like Mongoose middleware.
Subscribe to my newsletter
Read articles from Garry directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by