This is my original 2021 MERN tutorial, republished on this website. The examples are preserved. Tool versions and interfaces may have changed.A MongoDB-published version is available in the archived MERN tutorial from May 7, 2023. This tutorial will show you how to build a full-stack MERN application. Before you begin, make sure that you have Node and Create React App installed. You will also need access to the MongoDB Atlas database for this tutorial.
What Is the MERN Stack?

When you use the MERN stack, you work with React to implement the presentation layer, Express and Node to make up the middle or application layer and MongoDB to create the database layer. In this mern stack tutorial, we will utilize these four technologies to develop a basic mern stack application that is able to record the information of employees and then display it using a React.
How To Get Started with the MERN Stack
-
Install Node
To install node go to https://nodejs.org/en/ and download either the LTS version or the current version and install node. -
Have or Install a code editor
You can use any code editor of your choice for this tutorial. However, for the sake of demonstration, we will be using VS Code editor with the plugin prettier and vscode icons.
Setting Up the Project
$mkdir mern
$cd mern
$npx create-react-app client
Then we create a folder for the backend and name it server.
$mkdir server
We will jump into the server folder that we created previously and create the server. Then, we will initialize package.json using npm init.
$cd server
$npm init -y
We will also install the dependencies.
$npm install mongodb express cors dotenv
The command above uses a couple of keywords. Here,
- mongodb command installs MongoDB database driver that allows your Node.js applications to connect to the database and work with data.
- express installs for the web framework for Node.js (it will make our life easier),
- cors installs a node. js package that allows cross origin resource sharing.
- dotenv installs the module that loads environment variables from a .env file into process.env file. This lets you separate configuration files from the code.
const express = require("express");
const app = express();
const cors = require("cors");
require("dotenv").config({ path: "./config.env" });
const port = process.env.PORT || 5000;
app.use(cors());
app.use(express.json());
app.use(require("./routes/record"));
const dbo = require("./db/conn");
app.listen(port, () => {
dbo.connectToServer(function (err) {
if (err) console.error(err);
// start the rest of your app here
});
console.log(`Server is running on port: ${port}`);
});
Here, we are requiring express and cors to be used. const port process.env.port will access the port variable from the config.env we required.
As we move forward, we will be making changes to the server.js file, which will require us to restart the server multiple times. So to reduce the manual task of restarting the server, let’s install the nodemon module.
$npm install -g nodemon
$nodemon server
By installing the nodemon module, the server will automatically restart when changes to the source code is detected.
Connection to MongoDB Atlas
MongoDB Atlas provides a free tier cluster that never expires and lets you access a subset of Atlas features and functionality. To create a new database cluster:
- Sign in to your MongoDB Atlas account then, click on Build a cluster.
- Choose the region closest to you and click on Create Cluster.
- Go to Network access on the left hand side and add your IP address to the Whitelist.
- On the Database access, just above network access, add a database user. For this tutorial, we will add a user mern with the password mongodb.
- Go to Clusters and click on Connect.
- Choose Connect your application as your connection method.
- Copy the connection string. We will use it in our application to connect to MongoDB.
Now if you have the connection string, let’s go back to the server folder and create config.env file. There, we will paste the connection string to the ATLAS_URI variable. Once done, your file should look similar to the one below. Replace the <username> and the <password> with your database username and password.
ATLAS_URI=mongodb+srv://<username>:<password>@sandbox.jadwj.mongodb.net/myFirstDatabase?retryWrites=true&w=majority
PORT=3000
Create a folder under the server directory - db and inside it, a file - conn.js. There we can add the following code to connect to our database.
const { MongoClient } = require("mongodb");
const Db = process.env.ATLAS_URI;
const client = new MongoClient(Db, {
useNewUrlParser: true,
useUnifiedTopology: true,
});
var _db;
module.exports = {
connectToServer: function (callback) {
client.connect(function (err, db) {
_db = db.db("myFirstDatabase");
return callback(err);
});
},
getDb: function () {
return _db;
},
};
If you run the application at this point, you will get the following message in your terminal as the connection establishes.
Server API Endpoints
const express = require("express");
// recordRoutes is an instance of the express router.
// We use it to define our routes.
// The router will be added as a middleware and will take control of requests starting with path /record.
const recordRoutes = express.Router();
//This will help us connect to the database
const dbo = require("../db/conn");
// This section will help you get a list of all the records.
recordRoutes.route("/record").get(function (req, res) {
let db_connect = dbo.getDb("employees");
db_connect
.collection("records")
.find({})
.toArray(function (err, result) {
if (err) throw err;
res.json(result);
});
});
// This section will help you create a new record.
recordRoutes.route("/record/add").post(function (req, res) {
let db_connect = dbo.getDb("employees");
let myobj = {
person_name: req.body.person_name,
person_position: req.body.person_position,
person_level: req.body.person_level,
};
db_connect.collection("records").insertOne(myobj, function (err, res) {
if (err) throw err;
});
});
// This section will help you update a record by id.
recordRoutes.route("/update/:id").post(function (req, res) {
let db_connect = dbo.getDb("employees");
let myquery = { id: req.body.id };
let newvalues = {
$set: {
person_name: req.body.person_name,
person_position: req.body.person_position,
person_level: req.body.person_level,
},
};
db_connect
.collection("records")
.updateOne(myquery, newvalues, function (err, res) {
if (err) throw err;
console.log("1 document updated");
});
});
// This section will help you delete a record
recordRoutes.route("/:id").delete((req, res) => {
let db_connect = dbo.getDb("employees");
var myquery = { id: req.body.id };
db_connect.collection("records").deleteOne(myquery, function (err, obj) {
if (err) throw err;
console.log("1 document deleted");
});
});
module.exports = recordRoutes;
That’s it for the backend. Now, we will start working on the frontend.
How To Set Up the React Application
So let's move further and flesh out the application but before we do, we need to install two additional dependencies that will be used in our project.
Go to the client directory and install bootstrap and react-router-dom.
$npm install bootstrap
$npm install react-router-dom
By installing bootstrap you can quickly deploy a template and components for your new web application without having to do everything from scratch. And, the react-router-dom installs react router components for web applications. We will also let nodemon run in the background.
Setting Up React Router
import React from "react";
import ReactDOM from "react-dom";
import App from "./App";
import { BrowserRouter } from "react-router-dom";
ReactDOM.render(
<React.StrictMode>
<BrowserRouter>
<App />
</BrowserRouter>
</React.StrictMode>,
document.getElementById("root")
);
We have used BrowserRouter to keep our UI in sync with the URL. BrowserRouter helps with seamless transitions while switching between components. Basically, it will only reload/refresh the component that needs to be changed instead of refreshing/reloading the entire page. Though BrowserRouter is not a necessity, it is a must if you want your app to be responsive.
Creating Components
import React, { Component } from "react";
export default class Create extends Component {
// This is the constructor that stores the data.
constructor(props) {
super(props);
this.onChangePersonName = this.onChangePersonName.bind(this);
this.onChangePersonPosition = this.onChangePersonPosition.bind(this);
this.onChangePersonLevel = this.onChangePersonLevel.bind(this);
this.onSubmit = this.onSubmit.bind(this);
this.state = {
person_name: "",
person_position: "",
person_level: "",
};
}
// These methods will update the state properties.
onChangePersonName(e) {
this.setState({
person_name: e.target.value,
});
}
onChangePersonPosition(e) {
this.setState({
person_position: e.target.value,
});
}
onChangePersonLevel(e) {
this.setState({
person_level: e.target.value,
});
}
// This function will handle the submission.
onSubmit(e) {
e.preventDefault();
this.setState({
person_name: "",
person_position: "",
person_level: "",
});
}
// This following section will display the form that takes the input from the user.
render() {
return (
<div style={{ marginTop: 20 }}>
<h3>Create New Record</h3>
<form onSubmit={this.onSubmit}>
<div className="form-group">
<label>Name of the person: </label>
<input
type="text"
className="form-control"
value={this.state.person_name}
onChange={this.onChangePersonName}
/>
</div>
<div className="form-group">
<label>Person's position: </label>
<input
type="text"
className="form-control"
value={this.state.person_position}
onChange={this.onChangePersonPosition}
/>
</div>
<div className="form-group">
<div className="form-check form-check-inline">
<input
className="form-check-input"
type="radio"
name="priorityOptions"
id="priorityLow"
value="Intern"
checked={this.state.person_level === "Intern"}
onChange={this.onChangePersonLevel}
/>
<label className="form-check-label">Intern</label>
</div>
<div className="form-check form-check-inline">
<input
className="form-check-input"
type="radio"
name="priorityOptions"
id="priorityMedium"
value="Junior"
checked={this.state.person_level === "Junior"}
onChange={this.onChangePersonLevel}
/>
<label className="form-check-label">Junior</label>
</div>
<div className="form-check form-check-inline">
<input
className="form-check-input"
type="radio"
name="priorityOptions"
id="priorityHigh"
value="Senior"
checked={this.state.person_level === "Senior"}
onChange={this.onChangePersonLevel}
/>
<label className="form-check-label">Senior</label>
</div>
</div>
<div className="form-group">
<input
type="submit"
value="Create person"
className="btn btn-primary"
/>
</div>
</form>
</div>
);
}
}
edit.js
The following code will serve as an editing component for our records. It will use a similar layout to the create component and will eventually submit an update command to our server.
import React, { Component } from "react";
import { withRouter } from "react-router";
class Edit extends Component {
// This is the constructor that stores the data.
constructor(props) {
super(props);
this.onChangePersonName = this.onChangePersonName.bind(this);
this.onChangePersonPosition = this.onChangePersonPosition.bind(this);
this.onChangePersonLevel = this.onChangePersonLevel.bind(this);
this.onSubmit = this.onSubmit.bind(this);
this.state = {
person_name: "",
person_position: "",
person_level: "",
records: [],
};
}
// These methods will update the state properties.
onChangePersonName(e) {
this.setState({
person_name: e.target.value,
});
}
onChangePersonPosition(e) {
this.setState({
person_position: e.target.value,
});
}
onChangePersonLevel(e) {
this.setState({
person_level: e.target.value,
});
}
// This function will handle the submission.
onSubmit(e) {
e.preventDefault();
}
// This following section will display the update-form that takes the input from the user to update the data.
render() {
return (
<div>
<h3 align="center">Update Record</h3>
<form onSubmit={this.onSubmit}>
<div className="form-group">
<label>Person's Name: </label>
<input
type="text"
className="form-control"
value={this.state.person_name}
onChange={this.onChangePersonName}
/>
</div>
<div className="form-group">
<label>Position: </label>
<input
type="text"
className="form-control"
value={this.state.person_position}
onChange={this.onChangePersonPosition}
/>
</div>
<div className="form-group">
<div className="form-check form-check-inline">
<input
className="form-check-input"
type="radio"
name="priorityOptions"
id="priorityLow"
value="Intern"
checked={this.state.person_level === "Intern"}
onChange={this.onChangePersonLevel}
/>
<label className="form-check-label">Intern</label>
</div>
<div className="form-check form-check-inline">
<input
className="form-check-input"
type="radio"
name="priorityOptions"
id="priorityMedium"
value="Junior"
checked={this.state.person_level === "Junior"}
onChange={this.onChangePersonLevel}
/>
<label className="form-check-label">Junior</label>
</div>
<div className="form-check form-check-inline">
<input
className="form-check-input"
type="radio"
name="priorityOptions"
id="priorityHigh"
value="Senior"
checked={this.state.person_level === "Senior"}
onChange={this.onChangePersonLevel}
/>
<label className="form-check-label">Senior</label>
</div>
</div>
<br />
<div className="form-group">
<input
type="submit"
value="Update Record"
className="btn btn-primary"
/>
</div>
</form>
</div>
);
}
}
// You can get access to the history object's properties and the closest <Route>'s match via the withRouter
// higher-order component. This makes it easier for us to edit our records.
export default withRouter(Edit);
land.js
The following code will serve as a viewing component for our records. It will fetch all the records in our database through a GET method.
import React, { Component } from "react";
import { Link } from "react-router-dom";
const Record = (props) => (
<tr>
<td>{props.record.person_name}</td>
<td>{props.record.person_position}</td>
<td>{props.record.person_level}</td>
<td>
<Link to={"/edit/" + props.record._id}>Edit</Link> |
<a
href="/"
onClick={() => {
props.deleteRecord(props.record._id);
}}
>
Delete
</a>
</td>
</tr>
);
export default class RecordList extends Component {
// This is the constructor that shall store our data retrievedretrived from the database
constructor(props) {
super(props);
this.deleteRecord = this.deleteRecord.bind(this);
this.state = { records: [] };
}
// This method will map out the users on the table
recordList() {
return this.state.records.map((currentrecord) => {
return (
<Record
record={currentrecord}
deleteRecord={this.deleteRecord}
key={currentrecord._id}
/>
);
});
}
// This following section will display the table with the records of individuals.
render() {
return (
<div>
<h3>Record List</h3>
<table className="table table-striped" style={{ marginTop: 20 }}>
<thead>
<tr>
<th>Name</th>
<th>Position</th>
<th>Level</th>
<th>Action</th>
</tr>
</thead>
<tbody>{this.recordList()}</tbody>
</table>
</div>
);
}
}
navbar.js
In the navbar.js component, we will create a navigation bar that will link us to the required components using the following code.
import React from "react";
// We import bootstrap to make our application look better.
import "bootstrap/dist/css/bootstrap.css";
// We import NavLink to utilize the react router.
import { NavLink } from "react-router-dom";
// Here, we display our Navbar
const Navbar = () => {
return (
<div>
<nav className="navbar navbar-expand-lg navbar-light bg-light">
<NavLink className="navbar-brand" to="/">
MongoDB
</NavLink>
<button
className="navbar-toggler"
type="button"
data-toggle="collapse"
data-target="#navbarSupportedContent"
aria-controls="navbarSupportedContent"
aria-expanded="false"
aria-label="Toggle navigation"
>
<span className="navbar-toggler-icon"></span>
</button>
<div className="collapse navbar-collapse" id="navbarSupportedContent">
<ul className="navbar-nav ml-auto">
<li className="nav-item">
<NavLink className="nav-link" to="/create">
Create Record
</NavLink>
</li>
</ul>
</div>
</nav>
</div>
);
};
export default Navbar;
Now, we create a file App.js outside the components folder(In the same folder as index.js) and add the following code to utilize all the codes in our App.
import React from "react";
// We use Route in order to define the different routes of our application
import { Route } from "react-router-dom";
// We import all the components we need in our app
import Navbar from "./components/navbar";
import Edit from "./components/edit";
import Create from "./components/create";
import RecordList from "./components/land";
const App = () => {
return (
<div>
<Navbar />
<Route exact path="/">
<RecordList />
</Route>
<Route path="/edit/:id" component={Edit} />
<Route path="/create">
<Create />
</Route>
</div>
);
};
export default App;
Connecting Front-End to Back-End
$npm install axios
Once installed, we will add axios in create.js, edit.js and land.js as they will be handling http requests. We import axios by adding the following line right beneath import React from "react".
import axios from 'axios';
Then in create.js we will append the following code to the onSubmit(e) block. When a POST request is sent to the create URL, axios will add a new record to the database.
// This function will handle the submission.
onSubmit(e) {
e.preventDefault();
// When a post request is sent to the create url, axios will add a new record(newperson) to the database.
const newperson = {
person_name: this.state.person_name,
person_position: this.state.person_position,
person_level: this.state.person_level,
};
axios
.post("http://localhost:3000/record/add", newperson)
.then((res) => console.log(res.data));
// We will empty the state after posting the data to the database
this.setState({
person_name: "",
person_position: "",
person_level: "",
});
}
Similarly, in edit.js, we will also append the following code to the onSubmit(e) block.
// This function will handle the submission.
onSubmit(e) {
e.preventDefault();
const newEditedperson = {
person_name: this.state.person_name,
person_position: this.state.person_position,
person_level: this.state.person_level,
};
console.log(newEditedperson);
// This will send a post request to update the data in the database.
axios
.post(
"http://localhost:3000/update/" + this.props.match.params.id,
newEditedperson
)
.then((res) => console.log(res.data));
this.props.history.push("/");
}
We will also add the following block of code to edit.js beneath the constructor block.
// This will get the record based on the id from the database.
componentDidMount() {
axios
.get("http://localhost:3000/record/" + this.props.match.params.id)
.then((response) => {
this.setState({
person_name: response.data.person_name,
person_position: response.data.person_position,
person_level: response.data.person_level,
});
})
.catch(function (error) {
console.log(error);
});
}
Lastly, we have land.js. land.js fetches the records from the database, so we will be using axios’s get method to retrieve records from the database. In our case, the following code should work. We will also add the following lines of code above recordList() function in land.js.
// This method will get the data from the database.
componentDidMount() {
axios
.get("http://localhost:3000/record/")
.then((response) => {
this.setState({ records: response.data });
})
.catch(function (error) {
console.log(error);
});
}
// This method will delete a record based on the method
deleteRecord(id) {
axios.delete("http://localhost:3000/" + id).then((response) => {
console.log(response.data);
});
this.setState({
record: this.state.records.filter((el) => el._id !== id),
});
}
After closing everything, to start the app, follow these steps.
- Go into the server folder and run the command nodemon server.js
- Go back to the client folder and run the command npm start
This is what the screen that lets you add an employee will look like.
Congratulations on building your first MERN application.
FAQs
What can I build with the MERN stack?
Is MERN stack in demand?
Which is a better MEAN or MERN stack?
How do I become a MERN developer?
References
- “mern stack” https://www.mongodb.com/mern-stack
- “building Modern Apps with the MERN Stack and Google Cloud Platform” https://www.mongodb.com/presentations/building-modern-apps-with-the-mern-stack-and-google-cloud-platform
- “Part One: Building Web Apps with MERN Stack and Google Cloud Platform” https://www.mongodb.com/presentations/building-web-apps-mern-stack-google-cloud-platform