The original:
Let’s build a full stack of MongoDB, React, Node and Express (MERN) apps


By Jelo Rivera


Translator: Bo Xuan


In order to ensure readability, free translation rather than literal translation is used in this paper

When I want to move from front-end developer to full-stack developer, I have a hard time finding an article that covers all the concepts I need to learn.

For example, I am familiar with database, a back-end language, and how to integrate the front and back ends, which are still unfamiliar to me. That’s what drove me to finish this article: solving this problem to help myself and other front-end engineers.

The git repository address for the entire project is included at the end of this article, but I recommend that you learn the article step by step before looking at the project source code. This will help you understand the whole tutorial better. 😀

This is what our application looks like when it’s finished, and the front end allows us to add, delete, change, and check things.

We’re going to build this application from scratch. Set up the database, create the back end, and plug in the front end with minimal cost.

Next, let’s get ready and finish the project together!

Create a Client

Let’s create the home directory for the project. This will contain both front-end and back-end code for our application.

mkdir fullstack_app && cd fullstack_appCopy the code

So, let’s start with the front end. We’ll start building our front end using create-react-app, which means we don’t have to worry about Webpack and Babel configuration (since create-React-app does this by default). If you have not installed create-react-app globally, use the following command to install it.

sudo npm i -g create-react-appCopy the code

After that, we can use create-react-app to create our React application. Simply type the following command on the command line.

create-react-app client && cd client// The official recommendation is NPXcreate-react-app client
cd client
npm startCopy the code

We also need to use Axios to help us encapsulate get/ POST requests. Now let’s install it:

npm i -S axiosCopy the code

Once installed, we continue to organize the front end code so that we can plug in the back end later.

PC user:

del src\App.css src\App.test.js src\index.css src\logo.svg\Copy the code

MAC users:

rm src/App.css src/App.test.js src/index.css src/logo.svgCopy the code

Then, let’s edit our app.js file in the Client folder and let it just render something simple. We will edit this file further when we have the back end ready.

// client/src/App.js
import React, { Component } from "react";

class App extends Component {
  render() {
    return <div>I'M READY TO USE THE BACK END APIS! :-)</div>;
  }
}

export default App;Copy the code

We also need to edit index.js and remove a line of code. We need to delete ‘./index.css ‘; Now we can start our React application.

// client/src/index.js

import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';

ReactDOM.render(
  <App />,
  document.getElementById('root'));Copy the code

Now, just type on the command line:

npm startCopy the code

Next, open your browser and type http://localhost:3000/, and you can now see that our front end is up and running.

Create a Backend

Go back to our home directory and create our back-end directory from there. We’ll initialize this directory so that we can build the package.json we need when we’re ready. You’ll see some package.json configuration details on the terminal, just press Enter until you’re done.

mkdir backend && cd backend
npm init
// NPM init -y can also be used to speed up initializationCopy the code

Create a new file as the main code on the back end and name it server.js. Then, write the following into it. This part of the back-end code is very concise and basic, and I created it directly so that beginners can understand the intent of the code more quickly without having to worry about the complexity of the code. Then, once you understand the intent of the code, subsequent operations become easier. I’ve added comments next to each method to make it easier to understand.

const mongoose = require('mongoose');
const express = require('express');
var cors = require('cors');
const bodyParser = require('body-parser');
const logger = require('morgan');
const Data = require('./data');

const API_PORT = 3001;
const app = express();
app.use(cors());
const router = express.Router();

// This is our MongoDB database
const dbRoute =
  'mongodb://<your-db-username-here>:<your-db-password-here>@ds249583.mlab.com:49583/fullstack_app';

// Connect our back-end code to the database
mongoose.connect(dbRoute, { useNewUrlParser: true });

let db = mongoose.connection;

db.once('open'.(a)= > console.log('connected to the database'));

// Check whether the connection to the database is successful
db.on('error'.console.error.bind(console.'MongoDB connection error:'));

// (Optional) Only for recording and
// bodyParser, which parses the request body into readable JSON format
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
app.use(logger('dev'));

// This is our get method
// This method gets all available data in the database
router.get('/getData'.(req, res) = > {
  Data.find((err, data) = > {
    if (err) return res.json({ success: false, error: err });
    return res.json({ success: true, data: data });
  });
});

// This is our update method
// This method overwrites existing data in the database
router.post('/updateData'.(req, res) = > {
  const { id, update } = req.body;
  Data.findByIdAndUpdate(id, update, (err) = > {
    if (err) return res.json({ success: false, error: err });
    return res.json({ success: true });
  });
});

// This is our delete method
// This method deletes existing data in the database
router.delete('/deleteData'.(req, res) = > {
  const { id } = req.body;
  Data.findByIdAndRemove(id, (err) = > {
    if (err) return res.send(err);
    return res.json({ success: true });
  });
});

// This is how we create
// This method adds new data to our database
router.post('/putData'.(req, res) = > {
  let data = new Data();

  const { id, message } = req.body;

  if((! id && id ! = =0) | |! message) {return res.json({
      success: false,
      error: 'INVALID INPUTS'}); } data.message = message; data.id = id; data.save((err) = > {
    if (err) return res.json({ success: false, error: err });
    return res.json({ success: true });
  });
});

// Add/API for our HTTP request
app.use('/api', router);

// Send our backend to the port
app.listen(API_PORT, (a)= > console.log(`LISTENING ON PORT ${API_PORT}`));Copy the code

You may have noticed that links to the database are already set up in our back-end code. Don’t worry, this is the next step in our article. Setting it up will be just as easy as the previous steps. First, go to: MongoDB Atlas and create an account. MongoDB Atlas will provide us with a free 500MB MongoDB database. It’s hosted in the cloud, which is the current trend in our industry, and enables us to use our databases in the cloud.

After setting up the account, let’s log on to the website. Follow the instructions on the web site to create clusters and database administrators step by step. Here are the steps:

1. Build your first cluster



Create the first database user



3, will yourIPWhitelisted addresses (usually your native address)



4. Connect your cluster

We need to get the connection string for the database, so for step 4, all we need to do is click the connection button for the cluster we created, as shown below.

Then click “Choose a Connection Method” at the bottom of the popup and select “Connect Your Application”. Then, copy the string.

Paste this string URI into the server.js file. Find the dbRoute variable, replace the connection, and replace the username and password you set earlier.

Now, back to our back-end source code. We will now configure our database, creating a file called data.js. The code is as follows:

// /backend/data.js
const mongoose = require("mongoose");
const Schema = mongoose.Schema;

// this will be our data base's data structure 
const DataSchema = new Schema(
  {
    id: Number,
    message: String
  },
  { timestamps: true});// export the new Schema so we could modify it using Node.js
module.exports = mongoose.model("Data", DataSchema);Copy the code

At this point, we’re almost done! Let’s install the dependency for the back end using the following command:

npm i -S mongoose express body-parser morgan corsCopy the code

Start the backend:

node server.jsCopy the code

We can see in our console that it is ready and listening on port 3001. Let’s go back to the front end and complete the UI required for the MongoDB + Node.js + Express.js system.

Go back to /client/ SRC/app.js and make the following changes:

// /client/App.js
import React, { Component } from 'react';
import axios from 'axios';

class App extends Component {
  // Initialize the state of the component
  state = {
    data: [],
    id: 0,
    message: null,
    intervalIsSet: false,
    idToDelete: null,
    idToUpdate: null,
    objectToUpdate: null};// When the component loads, it first fetches all the data from the database, setting up a polling logic to update the data in the 'UI' in a timely manner.
  componentDidMount() {
    this.getDataFromDb();
    if (!this.state.intervalIsSet) {
      let interval = setInterval(this.getDataFromDb, 1000);
      this.setState({ intervalIsSet: interval }); }}// Never allow a process to persist
  // When we finish using it, we must kill the process
  componentWillUnmount() {
    if (this.state.intervalIsSet) {
      clearInterval(this.state.intervalIsSet);
      this.setState({ intervalIsSet: null}); }}// Our first get method using the back-end API
  // Retrieve data from our database
  getDataFromDb = () => {
    fetch('http://localhost:3001/api/getData')
      .then((data) = >data.json())
      .then((res) => this.setState({ data: res.data }));
  };

  // Use the put method to insert new data into the database
  putDataToDB = (message) => {
    let currentIds = this.state.data.map((data) = >data.id);
    let idToBeAdded = 0;
    while (currentIds.includes(idToBeAdded)) {
      ++idToBeAdded;
    }

    axios.post('http://localhost:3001/api/putData', {
      id: idToBeAdded,
      message: message,
    });
  };

  // Our delete method uses our back-end API
  // Delete existing database information
  deleteFromDB = (idTodelete) => {
    parseInt(idTodelete);
    let objIdToDelete = null;
    this.state.data.forEach((dat) => {
      if(dat.id == idTodelete) { objIdToDelete = dat._id; }}); axios.delete('http://localhost:3001/api/deleteData', {
      data: {
        id: objIdToDelete,
      },
    });
  };

  // Our update method uses our back-end API
  // Overwrite existing database information
  updateDB = (idToUpdate, updateToApply) => {
    let objIdToUpdate = null;
    parseInt(idToUpdate);
    this.state.data.forEach((dat) => {
      if(dat.id == idToUpdate) { objIdToUpdate = dat._id; }}); axios.post('http://localhost:3001/api/updateData', {
      id: objIdToUpdate,
      update: { message: updateToApply },
    });
  };

  render() {
    const { data } = this.state;
    return (
      <div>
        <ul>
          {data.length <= 0
            ? 'NO DB ENTRIES YET'
            : data.map((dat) => (
                <li style={{ padding: '10px' }} key={data.message}>
                  <span style={{ color: 'gray' }}> id: </span> {dat.id} <br />
                  <span style={{ color: 'gray'}} >data: </span>
                  {dat.message}
                </li>
              ))}
        </ul>
        <div style={{ padding: '10px' }}>
          <input
            type="text"
            onChange={(e) => this.setState({ message: e.target.value })}
            placeholder="add something in the database"
            style={{ width: '200px' }}
          />
          <button onClick={() => this.putDataToDB(this.state.message)}>
            ADD
          </button>
        </div>
        <div style={{ padding: '10px' }}>
          <input
            type="text"
            style={{ width: '200px' }}
            onChange={(e) => this.setState({ idToDelete: e.target.value })}
            placeholder="put id of item to delete here"
          />
          <button onClick={() => this.deleteFromDB(this.state.idToDelete)}>
            DELETE
          </button>
        </div>
        <div style={{ padding: '10px' }}>
          <input
            type="text"
            style={{ width: '200px' }}
            onChange={(e) => this.setState({ idToUpdate: e.target.value })}
            placeholder="id of item to update here"
          />
          <input
            type="text"
            style={{ width: '200px' }}
            onChange={(e) => this.setState({ updateToApply: e.target.value })}
            placeholder="put new value of the item here"
          />
          <button
            onClick={() =>
              this.updateDB(this.state.idToUpdate, this.state.updateToApply)
            }
          >
            UPDATE
          </button>
        </div>
      </div>
    );
  }
}

export default App;Copy the code

Finally, we edit package.json and add a proxy there that points to the back-end deployment port.

{
  "name": "client"."version": "0.1.0 from"."private": true."dependencies": {
    "axios": "^ 0.18.0." "."react": "^ 16.5.0"."react-dom": "^ 16.5.0"."react-scripts": "1.1.5."
  },
  "scripts": {
    "start": "react-scripts start"."build": "react-scripts build"."test": "react-scripts test --env=jsdom"."eject": "react-scripts eject"
  },
  "proxy": "http://localhost:3001"
}Copy the code

Keep in mind that this is the package.json for the front end and we need to edit it in the client directory.

Now, the last thing left is to start both the back-end and front-end projects.

To do this, let’s go back to the root of the project and type the following command:

npm init -y
npm i -S concurrentlyCopy the code

Edit package.json in the main project directory:

{
  "name": "fullstack_app"."version": "1.0.0"."description": ""."main": "index.js"."scripts": {
    "start": "concurrently \"cd backend && node server.js\" \"cd client && npm start\""
  },
  "keywords": []."author": ""."license": "ISC"."dependencies": {
    "concurrently": "^ 4.0.1." "}}Copy the code

Now, type NPM start on the command line to start our application. It will help us open the browser and see the MERN (full stack) application. We can extend any functionality we want on top of that.

Oh, one more thing. Make sure that CORS is enabled on the browser, which allows us to call our own apis from our own machine. This is a great plugin. 😬

Finally, here’s git repo.