Setting Up a MERN Stack App from Scratch in 2025

MERN Stack APP Setup Guide

The MERN stack APP — MongoDB, Express.js, React, and Node.js — continues to be one of the most powerful combinations for building full-stack JavaScript applications in 2025.

This modern stack allows developers to build scalable, maintainable, and dynamic web applications with a consistent language: JavaScript. In this guide, you’ll learn how to set up a MERN stack app from scratch using the latest tools and coding practices that align with 2025 standards.

Whether you’re a beginner learning to create your first full-stack app or an intermediate developer polishing your skills, this article will guide you through the process step-by-step with clean code examples, definitions of key terms, and explanations to help you understand what each section does.

What is MERN Stack?

The MERN stack APP is a JavaScript-based full-stack development framework made up of four powerful technologies:

  • MongoDB: A document-oriented NoSQL database used to store data in flexible, JSON-like documents.

  • Express.js: A lightweight Node.js web framework for building fast and simple APIs.

  • React: A powerful frontend JavaScript library maintained by Meta (Facebook) for building interactive user interfaces.

  • Node.js: A runtime that allows JavaScript to run on the server side.

These technologies work together to enable you to build everything from RESTful APIs to real-time applications — all using JavaScript.

MERN Stack APP Project Folder Structure

Organizing your files cleanly improves maintainability and team collaboration. A good structure for a MERN stack app looks like this:

mern-app/
├── backend/
│ ├── controllers/
│ ├── models/
│ ├── routes/
│ ├── config/
│ └── server.js
├── frontend/
│ └── src/
└── .env

Each directory serves a purpose: controllers/ holds business logic, models/ defines MongoDB schemas, and routes/ contains route definitions. Keeping concerns separated ensures a modular architecture.

Step 1: MERN Stack APP Backend Setup (Express + MongoDB)

We start by setting up the MERN stack app backend using Node.js and Express.js. This server will connect to a MongoDB database, expose a set of RESTful APIs, and serve as the foundation for our frontend app to interact with.

1) Installation: Open a terminal and run the following command to set up the necessary Node JS libraries, such as Mongoose for MongoDB and Express for the Express JS framework.

mkdir mern-app && cd mern-app
npx mkdirp backend/{routes,models,controllers,config}
cd backend
npm init -y
npm install express mongoose dotenv cors

2) Create a server.js file inside the backend directory and add the following code:

const express = require('express');
const dotenv = require('dotenv');
const cors = require('cors');
const taskRoutes = require('./routes/taskRoutes');
const connectDb = require("./config/db");
dotenv.config();
const app = express();

const PORT = process.env.PORT || 5000;
// Middleware
app.use(cors());
app.use(express.json());

// Routes
app.use('/api/tasks', taskRoutes);

// MongoDB Connection
connectDb().then(() => {
  app.listen(PORT, () => {
    console.log(`server is running at port: ${PORT}`);
  });
});

3) Create a db.js file inside the config directory under backend directory for MongoDB Connection

const mongoose = require("mongoose");
const dotenv = require('dotenv');
dotenv.config();
// mongoose.connect(URI);
const URI = process.env.MONGO_URI;
const connectDb = async () => {
  try {
    console.log(URI)
    await mongoose.connect(URI);
    console.log("connection successful to DB");
  } catch (error) {
    console.error("database connection failed");
    process.exit(0);
  }
};
module.exports = connectDb;

4) Environment Variables: Create a .env file at the root and write the following code. Also, replace <db_password> with the password for the umangapps48 database user.  you can learn more about this on https://www.mongodb.com/docs/drivers/node/current/fundamentals/connection/connect/#std-label-node-connect-to-mongodb article.

PORT=5000
MONGO_URI=mongodb+srv://umangapps48:<db_password>@cluster0.nwtloap.mongodb.net/?retryWrites=true&w=majority&appName=Cluster0

Backend Summary

In this step, we’ve installed the necessary dependencies (express, mongoose, cors, and dotenv) to build our backend. We configured our Express server to handle JSON and CORS, and set up a database connection to MongoDB using Mongoose. This setup forms the core of the backend server that can interact with a database and serve RESTful APIs.

Step 2: Create the MongoDB Model

Models in Mongoose define how data is structured in MongoDB. In this example, we’ll create a simple task model with two fields: title and completed.

1). Create Task model: Create backend/models/Task.js file and write the following code in Task.js file:

const mongoose = require('mongoose');
const taskSchema = new mongoose.Schema({
  title: { type: String, required: true },
  completed: { type: Boolean, default: false },
}, { timestamps: true });
module.exports = mongoose.model('Task', taskSchema);

Model Summary

This schema defines a MongoDB document for a task. title is a required string, and completed is a boolean, defaulting to false. The timestamps option automatically adds createdAt and updatedAt fields to each document — useful for tracking changes.

Step 3: Define Routes & Controllers

We’ll now create controller functions to handle CRUD operations and set up corresponding Express routes.

1) Controller logic: Create a taskController.js file in the backend/controllers/ directory

const Task = require('../models/Task');

// GET all tasks
const getTasks = async (req, res) => {
  const tasks = await Task.find();
  res.status(200).json(tasks);
};

// CREATE new task
const createTask = async (req, res) => {
  const task = await Task.create(req.body);
  res.status(201).json(task);
};

// UPDATE task by ID
const updateTask = async (req, res) => {
  const task = await Task.findByIdAndUpdate(req.params.id, req.body, { new: true });
  res.status(200).json(task);
};

// DELETE task
const deleteTask = async (req, res) => {
  await Task.findByIdAndDelete(req.params.id);
  res.status(204).end();
};

module.exports = {
  getTasks,
  createTask,
  updateTask,
  deleteTask
};

2). Define Route: Create a taskRoutes.js file in the backend/routes/ directory

const express = require('express');
const {
  getTasks,
  createTask,
  updateTask,
  deleteTask
} = require('../controllers/taskController');

const router = express.Router();
router.get('/', getTasks);
router.post('/', createTask);
router.put('/:id', updateTask);
router.delete('/:id', deleteTask);

module.exports = router;

Routes Summary

This part of the app exposes four key endpoints: GET (fetch all tasks), POST (create a new task), PUT (update a task by ID), and DELETE (Remove a task). The controllers abstract logic from routes, following the MVC pattern, making the app easier to maintain.

Once, backend of the MERN stack app is created with the above steps, you can run the “npm start” command in your terminal and use the Postman app you perform CRUD operations. For example, run “http://localhost:5000/api/tasks/” HTTP GET request to display all tasks data from the MongoDB database. Similarly, you can use other requests like the following to do other CRUD operations:

Term Meaning
GET /api/tasks Fetch all tasks
POST Create a new task
PUT Update existing task by ID
DELETE Remove task by ID
req.params.id Gets ID from URL like /tasks/:id

See screenshot below from Postman and MongoDB for the output

Mern Stack APP MongoDB MERN Stack App Postman

 

Step 4: MERN Stack APP Frontend with React

We’ll now build the frontend using Create React App. The UI will fetch and display tasks using Axios and React Hooks.

npm install axios
npm create vite@latest frontend

It will ask:

  • Project namefrontend

  • FrameworkReact

  • VariantJavaScript (or TypeScript if you prefer)

Then:

cd frontend
npm install
npm run dev

Done! Your modern React app is running at http://localhost:5173/

2). Edit src/App.jsx file

import React, { useEffect, useState } from 'react';
import axios from 'axios';

function App() {
  const [tasks, setTasks] = useState([]);
  const [newTask, setNewTask] = useState('');
  const fetchTasks = async () => {
    const res = await axios.get('http://localhost:5000/api/tasks');
    setTasks(res.data);
  };

  const addTask = async () => {
    if (!newTask.trim()) return;
    await axios.post('http://localhost:5000/api/tasks', { title: newTask });
    setNewTask('');
    fetchTasks();
  };

  const deleteTask = async (id) => {
    await axios.delete(`http://localhost:5000/api/tasks/${id}`);
    fetchTasks();
  };

  useEffect(() => {
    fetchTasks();
  }, []);

  return (
    <div className="container">
      <h1> Task Manager</h1>
      <div>
        <input
          type="text"
          placeholder="New Task"
          value={newTask}
          onChange={(e) => setNewTask(e.target.value)}
        />
        <button onClick={addTask}>Add Task</button>
      </div>
      <ul>
        {tasks.map(task => (
          <li key={task._id}>
            {task.title}
            <button onClick={() => deleteTask(task._id)}>❌</button>
          </li>
        ))}
      </ul>
    </div>
  );
}
export default App;

Summary:

  • Using axios for API calls.

  • useState manages tasks and new task input.

  • useEffect loads tasks on page load.

  • Clean separation of functions (fetch, add, delete).

3).  Edit src/index.css file

body {
font-family: sans-serif;
background-color: #f9f9f9;
padding: 2rem;
}
.container {
max-width: 600px;
margin: 0 auto;
}
input {
padding: 0.5rem;
margin-right: 0.5rem;
}
button {
padding: 0.5rem 1rem;
margin-left: 0.5rem;
}
ul {
list-style-type: none;
padding: 0;
}
li {
margin-top: 1rem;
background: #fff;
padding: 1rem;
border-radius: 5px;
display: flex;
justify-content: space-between;
}

Summary:

  • Simple responsive styling.

  • Clean layout for better user experience.

Frontend Summary

This frontend app uses Axios to fetch tasks from the backend API and displays them. We used useEffect() to load tasks when the component mounts, and useState() to store the data. The UI updates in real time and clearly displays the task list with simple indicators.

Step 5: Running the MERN stack App

At this point, your MERN stack app should be fully functional. The backend listens for API requests and connects to MongoDB, while the frontend fetches and renders the data beautifully. This is a complete MERN stack app built using modern practices.

Now, you need to run both projects:

In two terminals:

cd backend
npm start

and

cd frontend
npm run dev

So, on the first terminal, it runs the backend of the application, and on the other terminal,l it runs the frontend of the application. That’s it. This is how we set up a MERN stack app. See below for a screenshot when running the frontend app using “http://localhost:5173/” URL on a browser.

MERN Stack APP Frontend

References:

Conclusion:

Setting up a MERN stack app involves understanding each layer of the stack: the backend API with Express and MongoDB, and a dynamic frontend with React. With the help of this detailed guide, you’ve learned to structure your project using best practices, write clear and maintainable code, and build a seamless task manager app.

This tutorial is ideal for beginners who want to build a real-world MERN stack app and intermediate developers seeking to reinforce their skills using the latest standards in full-stack development.

Write a Reply or Comment

Your email address will not be published. Required fields are marked *