React.js + Node.js + Express + MongoDB example: MERN stack CRUD App

In this tutorial, we’re gonna build a MERN stack (React.js + Node.js + Express + MongoDB) CRUD Application example. The back-end server uses Node.js + Express for REST APIs, front-end side is a React client with React Router, Axios & Bootstrap.

Related Posts:
MERN stack Authentication & Authorization
React File Upload with Axios and Progress Bar to Rest API

Run both projects in one place:
How to integrate React with Node.js Express on same Server/Port

Dockerize: Docker Compose: React, Node.js Express, MongoDB example


React.js + Node.js + Express + MongoDB example Overview

We will build a full-stack Tutorial Application in that:

  • Tutorial has id, title, description, published status.
  • User can create, retrieve, update, delete Tutorials.
  • There is a search box for finding Tutorials by title.

– Add an item:

react-node-express-mongodb-mern-stack-example-create

– Show all items:

react-node-express-mongodb-mern-stack-example-retrieve

– Click on Edit button to view details of an item:

react-node-express-mongodb-mern-stack-example-retrieve-one

On this Page, you can:

  • change status to Published/Pending using Publish/UnPublished button
  • remove the object from MongoDB Database using Delete button
  • update this object’s details on Database with Update button

react-node-express-mongodb-mern-stack-example-update

– Search items by field ‘title’:

react-node-express-mongodb-mern-stack-example-search

– Check MongoDB Database:

react-node-express-mongodb-mern-stack-example-database

MERN stack Architecture

Our React.js + Node.js + Express + MongoDB application will follow this architecture:

react-node-express-mongodb-mern-stack-example-architecture

– Node.js Express exports REST APIs & interacts with MongoDB Database using Mongoose ODM.
– React Client sends HTTP Requests and retrieves HTTP Responses using Axios, consumes data on the components. React Router is used for navigating to pages.

Video

This is brief instruction on React Node.js Express application running with MongoDB database.

Node.js Express MongoDB Back-end

Overview

These are APIs that Node.js Express App will export:

Methods Urls Actions
GET api/tutorials get all Tutorials
GET api/tutorials/:id get Tutorial by id
POST api/tutorials add new Tutorial
PUT api/tutorials/:id update Tutorial by id
DELETE api/tutorials/:id remove Tutorial by id
DELETE api/tutorials remove all Tutorials
GET api/tutorials?title=[kw] find all Tutorials which title contains 'kw'

Project Structure

react-node-express-mongodb-mern-stack-example-server-project-structure

db.config.js exports configuring parameters for MongoDB connection & Mongoose.
Express web server in server.js where we configure CORS, initialize & run Express REST APIs.
– Next, we add configuration for MongoDB database in models/index.js, create Mongoose data model in models/tutorial.model.js.
– Tutorial controller in controllers.
– Routes for handling all CRUD operations (including custom finder) in tutorial.routes.js.

Implementation

Create Node.js App

First, we create a folder:

$ mkdir nodejs-express-mongodb
$ cd nodejs-express-mongodb

Next, we initialize the Node.js App with a package.json file:

npm init

name: (nodejs-express-mongodb) 
version: (1.0.0) 
description: Node.js Restful CRUD API with Node.js, Express and MongoDB
entry point: (index.js) server.js
test command: 
git repository: 
keywords: nodejs, express, mongodb, rest, api
author: bezkoder
license: (ISC)

Is this ok? (yes) yes

We need to install necessary modules: express, mongoose, body-parser and cors.
Run the command:

npm install express mongoose body-parser cors --save

Setup Express web server

In the root folder, let’s create a new server.js file:

const express = require("express");
const bodyParser = require("body-parser");
const cors = require("cors");

const app = express();

var corsOptions = {
  origin: "http://localhost:8081"
};

app.use(cors(corsOptions));

// parse requests of content-type - application/json
app.use(bodyParser.json());

// parse requests of content-type - application/x-www-form-urlencoded
app.use(bodyParser.urlencoded({ extended: true }));

// simple route
app.get("/", (req, res) => {
  res.json({ message: "Welcome to bezkoder application." });
});

// set port, listen for requests
const PORT = process.env.PORT || 8080;
app.listen(PORT, () => {
  console.log(`Server is running on port ${PORT}.`);
});

What we do are:
– import express, body-parser and cors modules:

  • Express is for building the Rest apis
  • body-parser helps to parse the request and create the req.body object
  • cors provides Express middleware to enable CORS with various options.

– create an Express app, then add body-parser and cors middlewares using app.use() method. Notice that we set origin: http://localhost:8081.
– define a GET route which is simple for test.
– listen on port 8080 for incoming requests.

Now let’s run the app with command: node server.js.
Open your browser with url http://localhost:8080/, you will see:

node-js-express-mongodb-example-setup-server

Yeah, the first step is done. We’re gonna work with Mongoose in the next section.

Configure MongoDB database & Mongoose

In the app folder, we create a separate config folder for configuration with db.config.js file like this:

module.exports = {
  url: "mongodb://localhost:27017/bezkoder_db"
};

Define Mongoose

We’re gonna define Mongoose model (tutorial.model.js) also in app/models folder in the next step.

Now create app/models/index.js with the following code:

const dbConfig = require("../config/db.config.js");

const mongoose = require("mongoose");
mongoose.Promise = global.Promise;

const db = {};
db.mongoose = mongoose;
db.url = dbConfig.url;
db.tutorials = require("./tutorial.model.js")(mongoose);

module.exports = db;

Don’t forget to call connect() method in server.js:

...
const app = express();
app.use(...);

const db = require("./app/models");
db.mongoose
  .connect(db.url, {
    useNewUrlParser: true,
    useUnifiedTopology: true
  })
  .then(() => {
    console.log("Connected to the database!");
  })
  .catch(err => {
    console.log("Cannot connect to the database!", err);
    process.exit();
  });

Define the Mongoose Model

In models folder, create tutorial.model.js file like this:

module.exports = mongoose => {
  const Tutorial = mongoose.model(
    "tutorial",
    mongoose.Schema(
      {
        title: String,
        description: String,
        published: Boolean
      },
      { timestamps: true }
    )
  );

  return Tutorial;
};

This Mongoose Model represents tutorials collection in MongoDB database. These fields will be generated automatically for each Tutorial document: _id, title, description, published, createdAt, updatedAt, __v.

{
  "_id": "5e363b135036a835ac1a7da8",
  "title": "Js Tut#",
  "description": "Description for Tut#",
  "published": true,
  "createdAt": "2020-02-02T02:59:31.198Z",
  "updatedAt": "2020-02-02T02:59:31.198Z",
  "__v": 0
}

If you use this app with a front-end that needs id field instead of _id, you have to override toJSON method that map default object to a custom object. So the Mongoose model could be modified as following code:

module.exports = mongoose => {
  var schema = mongoose.Schema(
    {
      title: String,
      description: String,
      published: Boolean
    },
    { timestamps: true }
  );

  schema.method("toJSON", function() {
    const { __v, _id, ...object } = this.toObject();
    object.id = _id;
    return object;
  });

  const Tutorial = mongoose.model("tutorial", schema);
  return Tutorial;
};

And the result will look like this-

{
  "title": "Js Tut#",
  "description": "Description for Tut#",
  "published": true,
  "createdAt": "2020-02-02T02:59:31.198Z",
  "updatedAt": "2020-02-02T02:59:31.198Z",
  "id": "5e363b135036a835ac1a7da8"
}

After finishing the steps above, we don’t need to write CRUD functions, Mongoose Model supports all of them:

These functions will be used in our Controller.

Create the Controller

Inside app/controllers folder, let’s create tutorial.controller.js with these CRUD functions:

  • create
  • findAll
  • findOne
  • update
  • delete
  • deleteAll
  • findAllPublished
const db = require("../models");
const Tutorial = db.tutorials;

// Create and Save a new Tutorial
exports.create = (req, res) => {
  
};

// Retrieve all Tutorials from the database.
exports.findAll = (req, res) => {
  
};

// Find a single Tutorial with an id
exports.findOne = (req, res) => {
  
};

// Update a Tutorial by the id in the request
exports.update = (req, res) => {
  
};

// Delete a Tutorial with the specified id in the request
exports.delete = (req, res) => {
  
};

// Delete all Tutorials from the database.
exports.deleteAll = (req, res) => {
  
};

// Find all published Tutorials
exports.findAllPublished = (req, res) => {
  
};

You can continue with step by step to implement this Node.js Express App in the post:
Node.js, Express & MongoDb: Build a CRUD Rest Api example

Run the Node.js Express Server

Run our Node.js application with command: node server.js.

React.js Front-end

Overview

react-node-express-mongodb-mern-stack-example-react-components-overview

– The App component is a container with React Router. It has navbar that links to routes paths.

TutorialsList component gets and displays Tutorials.
Tutorial component has form for editing Tutorial’s details based on :id.
AddTutorial component has form for submission new Tutorial.

– These Components call TutorialDataService methods which use axios to make HTTP requests and receive responses.

Or you can use React with Redux:

react-redux-crud-example-rest-api-axios-app-components

More details at: React Redux CRUD App example with Rest API

Technology

  • React 18/17
  • react-router-dom 6
  • axios 0.27.2
  • bootstrap 4

Project Structure

react-node-express-mongodb-mern-stack-example-client-project-structure

package.json contains 4 main modules: react, react-router-dom, axios & bootstrap.
App is the container that has Router & navbar.
– There are 3 components: TutorialsList, Tutorial, AddTutorial.
http-common.js initializes axios with HTTP base Url and headers.
TutorialDataService has methods for sending HTTP requests to the Apis.
.env configures port for this React CRUD App.

For Typescript version:

Please visit:
React Typescript CRUD example with Web API

Implementation

Setup React.js Project

Open cmd at the folder you want to save Project folder, run command:
npx create-react-app react-crud

After the process is done. We create additional folders and files like the following tree:


public

src

components

add-tutorial.component.js

tutorial.component.js

tutorials-list.component.js

services

tutorial.service.js

App.css

App.js

index.js

package.json


Import Bootstrap to React CRUD App

Run command: npm install bootstrap.

Open src/App.js and modify the code inside it as following-

import React, { Component } from "react";
import "bootstrap/dist/css/bootstrap.min.css";

class App extends Component {
  render() {
    // ...
  }
}

export default App;

Add React Router to React CRUD App

– Run the command: npm install react-router-dom.
– Open src/index.js and wrap App component by BrowserRouter object.

import React from "react";
import { createRoot } from "react-dom/client";
import { BrowserRouter } from "react-router-dom";

import App from "./App";

const container = document.getElementById("root");
const root = createRoot(container);

root.render(
  <BrowserRouter>
    <App />
  </BrowserRouter>
);

Add Navbar to React CRUD App

Open src/App.js, this App component is the root container for our application, it will contain a navbar, and also, a Routes object with several Route. Each Route points to a React Component.

import React, { Component } from "react";
...

class App extends Component {
  render() {
    return (
      <div>
        <nav className="navbar navbar-expand navbar-dark bg-dark">
          <a href="/tutorials" className="navbar-brand">
            bezKoder
          </a>
          <div className="navbar-nav mr-auto">
            <li className="nav-item">
              <Link to={"/tutorials"} className="nav-link">
                Tutorials
              </Link>
            </li>
            <li className="nav-item">
              <Link to={"/add"} className="nav-link">
                Add
              </Link>
            </li>
          </div>
        </nav>

        <div className="container mt-3">
          <Routes>
            <Route path="/" element={<TutorialsList/>} />
            <Route path="/tutorials" element={<TutorialsList/>} />
            <Route path="/add" element={<AddTutorial/>} />
            <Route path="/tutorials/:id" element={<Tutorial/>} />
          </Routes>
        </div>
      </div>
    );
  }
}

export default App;

Initialize Axios for React CRUD HTTP Client

Let’s install axios with command: npm install axios.
Under src folder, we create http-common.js file with following code:

import axios from "axios";

export default axios.create({
  baseURL: "http://localhost:8080/api",
  headers: {
    "Content-type": "application/json"
  }
});

You can change the baseURL that depends on REST APIs url that your Server configures.

Create Data Service

In this step, we’re gonna create a service that uses axios object above to send HTTP requests.

services/tutorial.service.js

import http from "../http-common";

class TutorialDataService {
  getAll() {
    return http.get("/tutorials");
  }

  get(id) {
    return http.get(`/tutorials/${id}`);
  }

  create(data) {
    return http.post("/tutorials", data);
  }

  update(id, data) {
    return http.put(`/tutorials/${id}`, data);
  }

  delete(id) {
    return http.delete(`/tutorials/${id}`);
  }

  deleteAll() {
    return http.delete(`/tutorials`);
  }

  findByTitle(title) {
    return http.get(`/tutorials?title=${title}`);
  }
}

export default new TutorialDataService();

We call axios get, post, put, delete method corresponding to HTTP Requests: GET, POST, PUT, DELETE to make CRUD Operations.

Create React Components/Pages

Now we’re gonna build 3 components corresponding to 3 Routes defined before.

  • Add new Item
  • List of items
  • Item details

You can continue with step by step to implement this React App in the post:
React.js CRUD example to consume Web API
– or React Hooks CRUD example to consume Web API

Using React with Redux:
React Redux CRUD example with Rest API
React Hooks + Redux: CRUD example with Rest API

For Typescript version:
React Typescript CRUD example to consume Web API

Run React CRUD App

You can run our App with command: npm start.
If the process is successful, open Browser with Url: http://localhost:8081/ and check it.

Source Code

You can find Github source code for this tutorial at: React + Node App Github

Conclusion

Today we have an overview of MERN stack example (React.js + Node.js Express + MongoDB) when building CRUD Application.

We also take a look at client-server architecture for REST API using Express & Mongoose ODM, as well as React.js project structure for building a front-end app to make HTTP requests and consume responses.

Next tutorials show you more details about how to implement the system (including source code):
Back-end
– Front-end:

You will want to know how to run both projects in one place:
How to integrate React with Node.js Express on same Server/Port

With Pagination:
React Pagination with API using Material-UI
Server side Pagination in Node.js, MongoDB | Mongoose Paginate v2

react-pagination-with-api-material-ui-change-page

Security: MERN stack Authentication & Authorization

Or Serverless with Firebase:
React Firebase CRUD with Realtime Database
React Firestore CRUD App example | Firebase Cloud Firestore

Happy learning, see you again!

Further Reading

Dockerize: Docker Compose: React, Node.js Express, MongoDB example

30 thoughts to “React.js + Node.js + Express + MongoDB example: MERN stack CRUD App”

  1. is no one getting the “Uncaught TypeError: Cannot read properties of undefined (reading ‘params’)” error????

    1. Ignore my previous post. After looking at this for days it is now working, minutes after posting that I was getting the same error as you.

      First, I was using the wrong versions. I updated to match all versions from the package.json from the git source:
      “bootstrap”: “^4.4.1”,
      “react”: “^16.13.0”,
      “react-dom”: “^16.13.0”,
      “react-router-dom”: “^5.1.2”,
      “react-scripts”: “3.4.0”
      This solved some errors.

      Then it still wasn’t able to get the id because my id is stored as “_id”. I added the underscore to
      this.props.match.params._id and
      this.state.currentTutorial._id
      and now it’s working.

      1. in tutorial.component.js

        componentDidMount() {
        this.getTutorial(this.props.match.params.id);
        }

        this.props.match.params.id only works if you have react-router-dom below version 6/v6
        make sure to downgrade react-router dom using the command: npm install [email protected]

  2. Hi, I followed these clear instructions, but its missing the serviceWorker part. Therefore its not working.
    Can you please add this?

    1. Hi, you can get the Github source code in the tutorials I mention at Conclusion section for details 🙂

  3. Hi. How can we add a user feature such that only the use who created “added” the tutorial can edit and those who haven’t created it can view it?

  4. Hi, I built front end, back end successfully, but in integration part, the sync() is undefined. So on 8080 port, the database was not connected. I am using online mongoDB

  5. Nice tutorial.i would want to know in which port will the web browser be set?I am unable to open the localhost:8081/.Could you tell me how will it open a particular port?

  6. Hello. Thank you for sharing this Tutorial. It help me a lot!

    I am wondering if I could develop my app with your MERN-stack Source code and blog in Japanese.
    If its bad and bother you, please let me know.

  7. Hi, just wondering if you can point to or explain where we would connect this with the Node/Express/MongoDB project?

    I have both running separately but can’t seem to grasp where to connect Axios to.

  8. Hi,

    your tutorial is really helpful ! I am wondering if it is possible to combine this with the login application ? If yes, how I should do it ?

    Thank you in advance for your time.

  9. Hi bezkoder,

    I like your article, its well organised.

    Could you help me to get the source code.

    1. Hi, you can find source code in the next tutorials at Conclusion sections 🙂

Comments are closed to reduce spam. If you have any question, please send me an email.