Build a simple MERN Stack Application | CRUD using

Well, this is the era of MERN stack , so why not learn it.

What is MERN

  • M – Mern
  • E – Express
  • R – React
  • N -Node

Don’t be happy , a lot of more package and plugin will be used , apart from above.

So let’s learn how to build a simple full stack application using all the above 4 tech stacks

Prerequisite

Basic knowledge of Java script ,React , Node Js will suffice

Note – I will try to explain as clearly as I can so that even if you don’t have any prior knowledge, it wont be overwhelming for you

Step 1 – Setup frontend and backend first

Create a folder – mern/frontend and mern/backend

Go to mern/frontend and start your react app

npx create-react-app .

Go to mern/backend and start you node js server

npm init

Step 2 – Create you first API

First let install few dependencies –

npm i express dotenv cors
npm install --save-dev nodemon

Now got to your package.json file –

nodemon node js

Now write your first api –

server.js:

const express = require("express");
const app = express();

app.get("/", (req, res) => {
  res.send("api running");
});

app.listen(4000);

Hurray You ran your first API.

Step 3 – Install MongoDB locally

Download Link –

How to install

  • First install MongoDB community edition to your program files
  • Second Extra MongoDB shell to your C:// drive
  • Now set environment variable for both –

And now restart your pc and go to cmd and type : mongosh

If something like this appears , congrats , its done.

Note – You can also install mongodb compass if you want GUI like interface.

Step 4 – Connect mongodb with nodejs

  • Install mongoose package –
npm i mongoose
  • Now to go to cmd and check if your mongodb server is running or not (type: mongosh)
  • Create a .evn file and paste –

.env

PORT = 8000
URI = "mongodb://localhost:27017/demo2"
  • Go to server.js and add –
//Connect to mongodb database(locally)
const express = require("express");
const app = express();
const dotenv = require("dotenv");
const mongoose = require("mongoose");
dotenv.config();

mongoose
  .connect(process.env.URI)
  .then(() => {
    console.log("Connected Successfully");
    app.listen(process.env.PORT || 5000, (err) => {
      if (err) console.log(err);
      console.log(`running at port ${process.env.PORT}`);
    });
  })
  .catch((error) => console.log("Failed to connect", error));

Step 5 – Create a schema and model

Model – It helps is interaction with the database

Schema – It define the structure on what type of data is going to be stored in our databsae

Create a model – mern/backend/models/userDataModel.js

userDataModel.js

const mongoose = require("mongoose");

//Create Schema
const userDataSchema = new mongoose.Schema(
  {
    name: {
      type: String,
      required: true,
    },
    email: {
      type: String,
      unique: true,
      required: true,
    },
    age: {
      type: Number,
    },
  },
  { timestamps: true }
);

//Create Model
const userData = mongoose.model("UserData", userDataSchema);

module.exports = userData;

Now import this in our main file i.e server.js

Note - As soon as we import our model , the database is created in the mongodb server.
The database name will be same, as we have pass in URI 

In our case - 
URI = "mongodb://localhost:27017/demo2"

So here the database name will be "demo2"
const express = require("express");
const app = express();
const dotenv = require("dotenv");
const mongoose  = require("mongoose");
dotenv.config();

//model imported here
const userData = require("./models/userDataModel");

app.get("/", (req, res) => {
  res.send("api running");
});

//Connect to mongodb database(locally)
mongoose
  .connect(process.env.URI)
  .then(() => {
    console.log("Connected Successfully");
    app.listen(process.env.PORT || 5000, (err) => {
      if (err) console.log(err);
      console.log(`running at port ${process.env.PORT}`);
    });
  })
  .catch((error) => console.log("Failed to connect", error));

app.listen(4000);

To test database is create or not , go to cmd and type : show dbs

Boom! Database created successfully.

Step 6 – Create Operation

Now lets create a new file for all our api’s .

Go to mern/backend/routes/userDataRoute.js

const express = require("express");
const router = express.Router();
const userData = require("../models/userDataModel");

//CREATE
router.post("/", async (req, res) => {
  console.log(req.body);
  const { name, email, age } = req.body;
  try {
    const userAdded = await userData.create({
      name: name,
      email: email,
      age: age,
    });
    res.status(201).json(userAdded);
  } catch (error) {
    console.log(error);
    res.status(400).json({ error: error.message });
  }
});



module.exports = router;

Step 7 – Read Operation

//GET
router.get("/", async (req, res) => {
  try {
    const allUsers = await userData.find();

    res.status(200).json(allUsers);
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

Get single user

//GET SINGLE USER
router.get("/:id", async (req, res) => {
  const { id } = req.params;

  try {
    const singleUser = await userData.findById({ _id: id });
    res.status(200).json(singleUser);
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

Step 8 – Delete Operation

//DELETE
router.delete("/:id", async (req, res) => {
  const { id } = req.params;
  try {
    const deletedUser = await userData.findByIdAndDelete({ _id: id });
    res.status(201).json(deletedUser);
  } catch (error) {
    res.status(400).json({ error: error.message });
  }
});

Step 9 – Update Operation

Get single data

//UPDATE
router.patch("/edit/:id", async (req, res) => {
  const { id } = req.params;
  console.log("get body", req.body);
  console.log("get id", id);
  //const { name, email, age } = req.body;
  try {
    const updatedUser = await userData.findByIdAndUpdate(id, req.body, {
      new: true,
    });
    res.status(200).json(updatedUser);
  } catch (error) {
    res.status(400).json({ error: error.message });
  }
});

serves.js

const express = require("express");
const app = express();
const dotenv = require("dotenv");
const mongoose = require("mongoose");
dotenv.config();
const userDataRoute = require("./routes/userDataRoute");

app.use(express.json());

//Connect to mongodb database(locally)
mongoose
  .connect(process.env.URI)
  .then(() => {
    console.log("Connected Successfully");
    app.listen(process.env.PORT || 5000, (err) => {
      if (err) console.log(err);
      console.log(`running at port ${process.env.PORT}`);
    });
  })
  .catch((error) => console.log("Failed to connect", error));

app.use(userDataRoute);

Step 10 – Lets build frontend

First go to the folder – mern/frontend and type : npm start

How to Run React JS Project in Localhost - Microverse Blog

Hurray! you react server is running.

Step 11 – Setting up Routing using react-router-dom

First install react router dom –

npm i react-router-dom

Now create a component folder : mern/frontend/src/components and create 3 react component

  • Create.jsx (mern/frontend/src/components/Create.jsx)
  • Read.jsx (mern/frontend/src/components/Read.jsx)
  • Update.jsx (mern/frontend/src/components/Update.jsx)

Now go to App.js –

import "./App.css";
import { BrowserRouter, Route, Routes } from "react-router-dom";
import Create from "./components/Create";
import Read from "./components/Read";
import Update from "./components/Update";
import Navbar from "./components/Navbar";

function App() {
  return (
    <div className="App">
      <BrowserRouter>
        <Navbar />
        <Routes>
          <Route exact path="/" element={<Create />} />
          <Route path="/read" element={<Read />} />
          <Route path="/:id" element={<Update />} />
        </Routes>
      </BrowserRouter>
    </div>
  );
}

export default App;

Step 12- Create form and submit data to backend

To perform our first operation i.e create, we need to form in frontend where user can fill the data and that will be passed to backend and finally stored in database.

Create.jsx

import React, { useState } from "react";
import { useNavigate } from "react-router-dom";

const Create = () => {
  const [fname, setName] = useState("");
  const [email, setEmail] = useState("");
  const [age, setAge] = useState(0);
  const [error, setError] = useState("");

  const navigate = useNavigate();

  const handleSubmit = async (e) => {
    e.preventDefault();
    var addUser = { fname, email, age };
    console.log(addUser);

    const response = await fetch("http://localhost:8000/", {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
      },
      body: JSON.stringify(addUser),
    });

    const result = await response.json();

    if (!response.ok) {
      console.log(result.error);
      setError(result.error);
    }
    if (response.ok) {
      console.log(result);
      setName("");
      setEmail("");
      setAge(0);
      setError("");
      navigate("/read");
    }
  };

  return (
    <div class="container my-2">
      <h1 class="h1 text-center">Fill the data</h1>

      {error && <div class="alert alert-danger"> {error} </div>}
      <form className="form" onSubmit={handleSubmit}>
        <div class="mb-3">
          <label class="form-label">Name</label>
          <input
            type="text"
            class="form-control"
            value={fname}
            onChange={(e) => setName(e.target.value)}
          />
        </div>
        <div class="mb-3">
          <label class="form-label">Email address</label>
          <input
            type="email"
            class="form-control"
            value={email}
            onChange={(e) => setEmail(e.target.value)}
          />
        </div>
        <div class="mb-3">
          <label class="form-label">Age</label>
          <input
            type="number"
            class="form-control"
            value={age}
            onChange={(e) => setAge(e.target.value)}
          />
        </div>
        <button type="submit" class="btn btn-primary">
          Submit
        </button>
      </form>
    </div>
  );
};

export default Create;

Also, as soon as form is submitted, the page is navigated to read component ie. Read.jsx

Step 13 – Displaying all the data on the UI

Sample Structure to use

import React from "react";

const Read = () => {
  return (
    <div className="container my-2">
      <div className="row">
        <div className="col-3">
          <div class="card">
            <div class="card-body">
              <h5 class="card-title">Card title</h5>
              <h6 class="card-subtitle mb-2 text-muted">Email</h6>
              <p class="card-text">age</p>
              <a href="#" class="card-link">
                Edit
              </a>
              <a href="#" class="card-link">
                Delete
              </a>
            </div>
          </div>
        </div>
      </div>
    </div>
  );
};

export default Read;

Read.jsx –

import React, { useEffect, useState } from "react";

const Read = () => {
  const [data, setData] = useState();
  const [error, setError] = useState();

  async function getData() {
    const response = await fetch("http://localhost:8000");
    const result = await response.json();
    console.log("result..", result);
    if (!response.ok) {
      setError(result.error);
    }

    if (response.ok) {
      console.log(response.ok);
      setData(result);
      setError("");
    }
  }


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

  return (
    <div className="container my-2">
      {error && <div class="alert alert-danger"> {error} </div>}
      <div className="row">
        {data?.map((ele) => (
          <div key={ele._id} className="col-3">
            <div class="card">
              <div class="card-body">
                <h5 class="card-title">{ele.name}</h5>
                <h6 class="card-subtitle mb-2 text-muted">{ele.email}</h6>
                <p class="card-text">{ele.age}</p>
                <span class="card-link">Edit</span>

                <span class="card-link">Delete</span>
              </div>
            </div>
          </div>
        ))}
      </div>
    </div>
  );
};

export default Read;

Step 14 – Deleting the data

Read.jsx

import React, { useEffect, useState } from "react";

const Read = () => {
  const [data, setData] = useState();
  const [error, setError] = useState();

  async function handleDelete(id) {
    const response = await fetch(`http://localhost:8000/${id}`, {
      method: "DELETE",
    });

    const result1 = await response.json();
    if (!response.ok) {
      setError(result1.error);
    }
    if (response.ok) {
      console.log("deleted", response.ok);
      setError("Deleted Successfully");
      setTimeout(() => {
        setError("");
        getData();
      }, 1000);
    }
  }

  async function getData() {
    const response = await fetch("http://localhost:8000");
    const result = await response.json();
    console.log("result..", result);
    if (!response.ok) {
      setError(result.error);
    }

    if (response.ok) {
      setData(result);
      setError("");
    }
  }

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

  return (
    <div className="container my-2">
      {error && <div class="alert alert-danger"> {error} </div>}
      <div className="row">
        {data?.map((ele) => (
          <div key={ele._id} className="col-3">
            <div class="card">
              <div class="card-body">
                <h5 class="card-title">{ele.name}</h5>
                <h6 class="card-subtitle mb-2 text-muted">{ele.email}</h6>
                <p class="card-text">{ele.age}</p>
                <span class="card-link">Edit</span>

                <span class="card-link" onClick={() => handleDelete(ele._id)}>
                  Delete
                </span>
              </div>
            </div>
          </div>
        ))}
      </div>
    </div>
  );
};

export default Read;

Step 15 – Updating User data from frontend

  • First add link to update user button present in Read.jsx
  • On clicking it should redirect to Update.jsx
  • Now grab this id from the URL
  • Create getSingleData() function to received a single user data and populate it on the input field
  • Now create handleUpdate() function to send the updated data to the backend using PATCH/PUT request
  • Grab the response
  • Redirect to the Read.jsx

Update.jsx –

import React, { useEffect, useState } from "react";
import { useNavigate, useParams } from "react-router-dom";

const Update = () => {
  const [fname, setName] = useState("");
  const [email, setEmail] = useState("");
  const [age, setAge] = useState(0);

  const [error, setError] = useState();
  const { id } = useParams();
  console.log(id);

  const navigate = useNavigate();

  //receving single user data
  const getSingleData = async () => {
    const response = await fetch(`http://localhost:8000/${id}`);
    const result = await response.json();

    if (response.ok) {
      setName(result.name);
      setEmail(result.email);
      setAge(result.age);
    }
  };

  //passing edited data to backend
  const handleUpdate = async (e) => {
    e.preventDefault();
    const updatedUser = { fname, email, age };
    console.log(updatedUser);
    const response = await fetch(`http://localhost:8000/edit/${id}`, {
      method: "PATCH",
      headers: {
        "Content-Type": "application/json",
      },
      body: JSON.stringify(updatedUser),
    });
    const result = await response.json();
    if (response.ok) {
      console.log("updated result..", result);
      setError("");
      navigate("/read");
    }
    if (!response.ok) {
      console.log(response.error);
      setError(response.error);
    }
  };

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

  return (
    <div class="container my-2">
      <h1 class="h1 text-center">Edit Data</h1>
      {error && <div class="alert alert-danger"> {error} </div>}
      <form className="form" onSubmit={handleUpdate}>
        <div class="mb-3">
          <label class="form-label">Name</label>
          <input
            type="text"
            class="form-control"
            value={fname}
            onChange={(e) => setName(e.target.value)}
          />
        </div>
        <div class="mb-3">
          <label class="form-label">Email address</label>
          <input
            type="email"
            class="form-control"
            value={email}
            onChange={(e) => setEmail(e.target.value)}
          />
        </div>
        <div class="mb-3">
          <label class="form-label">Age</label>
          <input
            type="number"
            class="form-control"
            value={age}
            onChange={(e) => setAge(e.target.value)}
          />
        </div>
        <button type="submit" class="btn btn-info">
          Update
        </button>
      </form>
    </div>
  );
};

export default Update;

Step 16 – Adding Links of Navbar

Navbar.jsx

import React from "react";
import { Link } from "react-router-dom";

const Navbar = () => {
  return (
    <nav className="navbar navbar-expand-lg navbar-light bg-light">
      <div className="container-fluid">
        <h3 className="navbar-brand" href="#">
          MERN
        </h3>
        <button
          className="navbar-toggler"
          type="button"
          data-bs-toggle="collapse"
          data-bs-target="#navbarNav"
          aria-controls="navbarNav"
          aria-expanded="false"
          aria-label="Toggle navigation"
        >
          <span className="navbar-toggler-icon"></span>
        </button>
        <div className="collapse navbar-collapse" id="navbarNav">
          <ul className="navbar-nav">
            <li className="nav-item">
              <Link to="/" className="nav-link" aria-current="page">
                Create Post
              </Link>
            </li>
            <li className="nav-item">
              <Link to="/read" className="nav-link active" aria-current="page">
                All Post
              </Link>
            </li>
          </ul>
        </div>
      </div>
    </nav>
  );
};

export default Navbar;
Help Others

3 Comments

  1. Rishiraj Mukherjeesays:

    the overall given good is not at all completed. You need to configure the cors, bootstap and bunch of other stuffs in create.jsx to make sure everything works smoothly. If you face difficulty in debugging do tell me, Ill post the complete code

Leave a Reply

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