How to create a simple full-stack application with MERN stack

What is a MERN stack?

A MERN stack application consists of four components:

To create a simple full-stack MERN application, we must first set up the development environment.

Setup

We must configure and install the components mentioned above on our systems first.

NodeJS installation

We can install NodeJS on Windows or macOS by downloading the installer from here and running it.

To install NodeJS on Ubuntu, we must follow the steps mentioned here.

ReactJS installation

To install ReactJS, we must go to the desired folder where we want to create the application and open "cmd" in Windows or "terminal" in Linux/macOS. Then, we run the following command:

npx create-react-app <myapp>

Here, <myapp> will be replaced by a custom application name.

After a successful execution, this command creates a custom React application with some boilerplate code already available. We can verify the correct installation of our application by executing the following commands:

1. cd <myapp>
2. npm start

Our browser loads the boilerplate code and displays the react logo in a tab.

ExpressJS installation

To install ExpressJS, we open the React application directory in "terminal"/"cmd" and run the following command:

npm init

The above command creates a package.json file in the directory. For default settings, we just hit "RETURN."

After that, we run the following:

npm install express

This step installs Express in our application directory. The index.js file will have our Express code. We rename the file to Server.js.

MongoDB installation

We can install MongoDB in our systems by following the instructions on the official MongoDB website.

After the installation, we need to set up Mongoose, a tool that lets us work in an asynchronous environment.

The following command executes for Mongoose installation in our application:

npm install mongoose

After this command, we can see mongoose as a dependency in the package.json file.

The react-router-dom package installation

We need the react-router-dom package to enable navigation in our application. To install it, we run the following command:

npm install react-router-dom

Axios installation

Axios is an HTTPHyper Text Transfer Protocol promise-based client for NodeJS. We need Axios to handle APIApplication Programming Interface calls. To install Axios, we go to the application folder and run the following command:

npm install axios

This adds axios as a dependency in the package.json file.

Cors installation

Cors is used to provide express middlewareA package that provides external services . To install it, we run the following command in the application folder:

npm install cors

Application development

We will develop a simple 'To-Do List" application as follows:

Front-end development

First, let's start designing HTML pages and the react components we need.

Note: We do not consider styling in this application.

Our application will have two HTML pages. We will use one to add tasks, and the other one to display a to-do list.

Representation of the application's HTML pages

Let's start by creating React components and importing them to the HTML pages. To do this, we do as follows:

  • Create a folder inside the application's frontend directory and place all other files/folders inside this one. Delete the public folder and create a directory called components inside the src folder.

  • Add a .js file named TaskComponent in the components directory. It will represent a to-do task.

  • Design the TaskComponent using the functional React component.

  • Create another folder named pages inside the src folder. It will contain our HTML pages.

  • Add two files named addTask.js and ToDoList.js inside the newly created folder.

  • Design the addTask.js page.

  • Import the React component for the ToDoList.js page and design it.

  • Delete all the other files that we do not need.

Our application currently looks like this:

i=0
cd /backend/src && node Server.js &
while :
do
   curl {{EDUCATIVE_LIVE_VM_URL}}:3000/Tasks/getTasks >& log.txt;
   if grep "Your app refused to connect." < log.txt > waste.txt; then
   i=0
   else
      break;
   fi
done
The frontend directory representation

Explanation

In the component file:

  • Line 2: We use the react functional component to implement the task component. Inside that functional component, we design it and send props as the parameter.

  • Lines 10, 13: The props parameter contains the id and task (details) that we show on the page.

On the ToDoList.js page:

  • Lines 1–2: We import useEffect and axios as we want to display all the tasks on the first-page load.

  • Lines 6, 20: We import and return the task component in a map() function. This map() function runs the task component for all the tasks available in the database.

  • Lines 10–16: We use axios.get() to fetch data from the route passed in as the parameter and, in response, we set the task state as the list of tasks that is the return value of the axios.get() fetch request.

  • Line 14: The prop passed in as the parameter has the setTasks() method that will be used to update the list of tasks.

  • Line 26: Finally, we have a button that redirects us to the "add task" page (home page).

On the AddTask.js page:

  • Lines 7–29: It has a useState that tracks what the user inputs to the input field, and an event handler that adds the task details to the database using axios.post(). In the handleClick() event handler function, we create a new object based on the details input by the user. We use the POST request to send this object and save it in the database.

  • Lines 33–46: There is one input field that has the task description and two buttons named submit task and view list. We use them to add a task to the database and re-route to the ToDoList.js page.

In the app.js file:

  • Line 8: We have our primary state of tasks as it comes at the top in the file hierarchy.

  • Lines 10–17: Secondly, we have react-router routes that navigates to particular pages when a specific URL hit occurs. The tasks state is passed to the pages as props, along with its setter function, so that the state can be updated globally at any point of our application.

Back-end development

After implementing the frontend, let's focus on how to design and code the backend of our project. For this, we do the following:

  • Create a folder named backend in the application directory. Inside that folder, create a directory called src.

  • Add two new folders in the src directory named routes and schema.

Routes

This folder contains all of the CRUDCreate, read, update, and delete functions of our application. These steps are to be followed:

  • Add a .js file called tasks.js inside this folder.

  • Import express and run its router.

  • Create GET and POST APIs and assign URL routes to these APIs.

  • These APIs are responsible for the data flow between the user and the database.

  • Export the router as a module.

//creating express router
const express = require('express')
var router = express.Router()
//importing task model from taskSchema.js
const taskTable = require('../schema/taskSchema')
//api for fetching all tasks from db
router.get('/getTasks',function(req,res){
taskTable.find({},(err,tasks)=>{
if(err)
res.send(err)
else
res.send(tasks)
})
})
//api to add task to db
router.post('/addTask',async(req,res)=>{
const recv_id = req.body.id
const recv_details = req.body.details
var newTask = new taskTable({
id:recv_id,
details:recv_details
})
newTask.save((err,doc)=>{
if(err)
res.send("Cannot add task!")
else
res.send("Task added!")
})
})
module.exports=router;

Explanation

There are two routes in this file.

  • Lines 7–14: One route handles the GET request when the /getTasks URL hit occurs. In the GET request handler route, the find() query fetches all the tasks in the taskTable. If there is no error, the tasks are returned as a response.

  • Lines 16–29: The other handles the POST request when the /addTask URL hit occurs. We also import the taskTable to communicate with the database. In the POST request handler route, we create a new taskTable object based on the parameters received by the handler. We use the save() function to store the details in the database.

The schema folder

The schema folder has code that implements the database table for our tasks. Let's see the steps that we need to execute:

  • Add a file named taskSchema.js in the schema folder.

  • Import mongoose.

  • Create a new schema using the mongoose.Schema() function, which takes fields as parameters.

  • Create a table using the mongoose.model() function that has two parameters. The first is the title assigned to the task table, and the second is the schema we just created.

  • Export the model as a module.

const mongoose = require('mongoose')
//schema with two fields
const taskSchema = new mongoose.Schema({
id:Number,
details:String
})
const taskTable = new mongoose.model('Tasks',taskSchema)
module.exports = taskTable;

Explanation

  • Lines 3–6: We declare the taskSchema variable as a schema with the mongoose.Schema() function, where id and details are fields of this schema.

  • Line 7: The taskTable variable represents a table of the schema mentioned above, and we define it using the mongoose.model() function with the title being Tasks.

The Server.js file

We add the Server.js file in the backend directory. This file is responsible for handling our nodeJS server.

To create this file, we do as follows:

  • Import express and run the express() function for a variable.

  • Acquire cors.

  • The use() function of express utilizes all the resources needed to make our application work. This includes schemas, routes, and packages.

  • Establish a connection with the database using the mongoose.connect() function and giving its parameters.

  • Use our application routes.

  • The listen() function runs the server on the port we pass as the parameter.

//important imports
const express = require('express')
const app= express()
const cors = require('cors')
//for parsing
app.use(express.json())
app.use(express.urlencoded({ extended: true }))
app.use(cors())
//database connection
const mongoose = require('mongoose')
mongoose.connect('mongodb://localhost:27017/ToDOList',{
useNewUrlParser:true,
useUnifiedTopology:true
});
//using the apis
const taskRouter = require('./routes/tasks')
app.use('/Tasks',taskRouter);
//listening to port 3000
app.listen(3000,()=>{
console.log("Server is running on port: 3000")
})

Explanation

  • Lines 11–14: After the required imports, we connect to the database on port 27017. The name of our database is ToDOList .

  • Lines 16–21: Then, we make express use of our NodeJS routes by passing the taskRouter variable to the express variable named app and listening to the port 3000.

Complete application

The following widget represents our working application. Let's hit the "Run" button to see how our application works:

i=0
cd /backend/src && node Server.js &
while :
do
   curl {{EDUCATIVE_LIVE_VM_URL}}:3000/Tasks/getTasks >& log.txt;
   if grep "Your app refused to connect." < log.txt > waste.txt; then
   i=0
   else
      break;
   fi
done

Note: The application might take some time to load on the website.

Execution

We need two terminals to run the project on your machine. For this, we must follow the below steps:

  • cd to the backend folder and run this command: node Server.js. It will run the backend server.

  • cd to the frontend folder and run this command: npm start. The React server will run, and the application will be loaded on a browser tab.

Free Resources

Copyright ©2025 Educative, Inc. All rights reserved