DEV Community

Cover image for Making First Simple CRUD App with MERN-Stack
Gerwin Jo
Gerwin Jo

Posted on • Edited on

Making First Simple CRUD App with MERN-Stack

Today, I learn about making simple CRUD student app by using MERN Stack. MERN can be abbreviated with MongoDB, Express, React and Node.JS. MERN also can be called a full stack which include frontend and backend.

Results

  • List All Students
    Alt Text

  • Create Students
    Alt Text

You can check and run full repository on my GitHub: https://github.com/gerwinjonathan/school-app

Overview

  • MongoDB: General purpose, document-based, distributed database and popular database.
  • Express: Minimalist framework built for Node.js
  • React: UI framework designed for web application.
  • Node.JS: JavaScript runtime built for scalable application.

Preparation

First things First

  1. Make sure Node.JS is installed.
    Node.JS: https://nodejs.org/en/download/

  2. Install React App with NPX.

npx create-react-app school-app

  1. Install all package manager those are built for this project.

npm install mongoose express bootstrap axios reactstrap react-icons react-router-dom react-dom --save

* mongoose: tools for object modelling built for Node.JS (https://mongoosejs.com/)
* reactstrap: Bootstrap CSS for React (https://reactstrap.github.io/)
* react-icons: Icons for react (https://react-icons.github.io/react-icons/)
* axios: Promise based HTTP client for the browser and node.js
Enter fullscreen mode Exit fullscreen mode

Let's get Started - FrontEnd ⚡

  • Prepare your pages 📄

There are 4 pages those we want to build. Put it into components folder in src folder. Those are create-student.component, delete-student.component, edit-student.component, list-student.component.

  • create-Student.component.jsx
import React, { useState, useEffect } from 'react';
import { Form, FormGroup, Label, Input, Col, Button } from 'reactstrap';
import { AiOutlineUserAdd, AiOutlineUser, AiOutlineExport, AiOutlineForward } from 'react-icons/ai';
import axios from 'axios';

const CreateStudent = (props) => {
    const [data, setData] = useState({
        student_name: "",
        student_address: "",
        student_number: "",
        student_entry: "",
        student_year: "",
        student_verification: false
    });

    const onChangeStudentData = (e) => {
        setData({
            ...data,
            [e.target.name]: e.target.value
        })
    }

    const onSubmitStudentData = (e) => {
        e.preventDefault();
        axios.post('http://localhost:4000/all_student/add', data).then(res => console.log(res.data));
        setData({
            student_name: "",
            student_address: "",
            student_number: "",
            student_entry: "",
            student_year: "",
            student_verification: false
        });
    }

    return (
        <div style={{ marginTop: 10 }}>
            <h3><AiOutlineUserAdd /> Create Student</h3>
            <Form onSubmit={onSubmitStudentData}>
                <FormGroup row>
                    <Col>
                        <Label><AiOutlineUser /> Student Name </Label>
                        <Input
                            type="text"
                            name="student_name"
                            className="form-control"
                            value={data.student_name}
                            onChange={onChangeStudentData} />
                    </Col>
                </FormGroup>
                <FormGroup row>
                    <Col>
                        <Label><AiOutlineExport /> Address </Label>
                        <Input
                            type="text"
                            name="student_address"
                            className="form-control"
                            value={data.student_address}
                            onChange={onChangeStudentData} />
                    </Col>
                </FormGroup>
                <FormGroup row>
                    <Col>
                        <Label><AiOutlineExport /> Student Number </Label>
                        <Input
                            type="number"
                            name="student_number"
                            className="form-control"
                            value={data.student_number}
                            onChange={onChangeStudentData} />
                    </Col>
                </FormGroup>
                <FormGroup row>
                    <Col md={6}>
                        <Label><AiOutlineExport /> Entry Level </Label>
                        <Input
                            type="text"
                            name="student_entry"
                            className="form-control"
                            value={data.student_entry}
                            onChange={onChangeStudentData} />
                    </Col>
                    <Col md={6}>
                        <Label><AiOutlineExport /> Entry Year </Label>
                        <Input
                            type="number"
                            name="student_year"
                            className="form-control"
                            value={data.student_year}
                            onChange={onChangeStudentData} />
                    </Col>
                </FormGroup>
                <Button color="primary"><AiOutlineForward /> Submit</Button>
            </Form>
        </div>
    );
}

export default CreateStudent;
Enter fullscreen mode Exit fullscreen mode
  • delete-student.component.jsx
import React, { useState, useEffect } from 'react';
import { Form, FormGroup, Label, Input, Col, Button } from 'reactstrap';
import { AiOutlineUser, AiOutlineExport, AiOutlineDelete } from 'react-icons/ai';
import axios from 'axios';

const DeleteStudent = (props) => {
    const [data, setData] = useState({
        student_name: "",
        student_address: "",
        student_number: "",
        student_entry: "",
        student_year: "",
        student_verification: false
    });

    useEffect(() => {
        const fetchData = async () => {
            const result = await axios(
                `http://localhost:4000/all_student/${props.match.params.id}`
            );
            setData({ ...result.data });
        };
        fetchData();
    }, []);

    const onDeleteStudentData = (e) => {
        e.preventDefault();
        axios.delete(`http://localhost:4000/all_student/delete/${props.match.params.id}`, data).then(res => console.log(res.data));
        props.history.push('/');
    }

    return (
        <div style={{ marginTop: 10 }}>
            <h3>Delete Student</h3>
            <Form onSubmit={onDeleteStudentData}>
                <FormGroup row>
                    <Col>
                        <Label><AiOutlineUser /> Student Name </Label>
                        <Input
                            readOnly
                            type="text"
                            name="student_name"
                            className="form-control"
                            value={data.student_name} />
                    </Col>
                </FormGroup>
                <FormGroup row>
                    <Col>
                        <Label><AiOutlineExport /> Address </Label>
                        <Input
                            readOnly
                            type="text"
                            name="student_address"
                            className="form-control"
                            value={data.student_address} />
                    </Col>
                </FormGroup>
                <FormGroup row>
                    <Col>
                        <Label><AiOutlineExport /> Student Number </Label>
                        <Input
                            readOnly
                            type="number"
                            name="student_number"
                            className="form-control"
                            value={data.student_number}/>
                    </Col>
                </FormGroup>
                <FormGroup row>
                    <Col md={6}>
                        <Label><AiOutlineExport /> Entry Level </Label>
                        <Input
                            readOnly
                            type="text"
                            name="student_entry"
                            className="form-control"
                            value={data.student_entry} />
                    </Col>
                    <Col md={6}>
                        <Label><AiOutlineExport /> Entry Year </Label>
                        <Input
                            readOnly
                            type="number"
                            name="student_year"
                            className="form-control"
                            value={data.student_year}/>
                    </Col>
                </FormGroup>
                <Button color="danger"><AiOutlineDelete /> Delete Data</Button>
            </Form>
        </div>
    );
}

export default DeleteStudent;
Enter fullscreen mode Exit fullscreen mode
  • edit-student.component.jsx
import React, { useState, useEffect } from 'react';
import { Form, FormGroup, Label, Input, Col, Button } from 'reactstrap';
import { AiOutlineUserAdd, AiOutlineUser, AiOutlineExport, AiOutlineForward } from 'react-icons/ai';
import axios from 'axios';

const EditStudent = (props) => {
    const [data, setData] = useState({
        student_name: "",
        student_address: "",
        student_number: "",
        student_entry: "",
        student_year: "",
        student_verification: false
    });

    useEffect(() => {
        const fetchData = async () => {
            const result = await axios(
                `http://localhost:4000/all_student/${props.match.params.id}`
            );
            setData({ ...result.data });
        };
        fetchData();
    }, []);

    const onChangeStudentData = (e) => {
        setData({
            ...data,
            [e.target.name]: e.target.value
        })
        console.log(data);
    }

    const onSubmitStudentData = (e) => {
        e.preventDefault();
        axios.post(`http://localhost:4000/all_student/update/${props.match.params.id}`, data).then(res => console.log(res.data));
        props.history.push('/');
    }

    return (
        <div style={{ marginTop: 10 }}>
            <h3><AiOutlineUserAdd /> Edit Student</h3>
            <Form onSubmit={onSubmitStudentData}>
                <FormGroup row>
                    <Col>
                        <Label><AiOutlineUser /> Student Name </Label>
                        <Input
                            type="text"
                            name="student_name"
                            className="form-control"
                            value={data.student_name}
                            onChange={onChangeStudentData} />
                    </Col>
                </FormGroup>
                <FormGroup row>
                    <Col>
                        <Label><AiOutlineExport /> Address </Label>
                        <Input
                            type="text"
                            name="student_address"
                            className="form-control"
                            value={data.student_address}
                            onChange={onChangeStudentData} />
                    </Col>
                </FormGroup>
                <FormGroup row>
                    <Col>
                        <Label><AiOutlineExport /> Student Number </Label>
                        <Input
                            type="number"
                            name="student_number"
                            className="form-control"
                            value={data.student_number}
                            onChange={onChangeStudentData} />
                    </Col>
                </FormGroup>
                <FormGroup row>
                    <Col md={6}>
                        <Label><AiOutlineExport /> Entry Level </Label>
                        <Input
                            type="text"
                            name="student_entry"
                            className="form-control"
                            value={data.student_entry}
                            onChange={onChangeStudentData} />
                    </Col>
                    <Col md={6}>
                        <Label><AiOutlineExport /> Entry Year </Label>
                        <Input
                            type="number"
                            name="student_year"
                            className="form-control"
                            value={data.student_year}
                            onChange={onChangeStudentData} />
                    </Col>
                </FormGroup>
                <FormGroup check row>
                    <Col>
                        <Label check>
                            <Input
                                type="checkbox"
                                name="student_verification"
                                defaultChecked={data.student_verification ? true : false}
                                value={data.student_verification ? Boolean(true) : Boolean(false)}
                                onChange={onChangeStudentData}
                                required />{data.student_verification ? "Data is verified" : "Data isn't verified"}
                        </Label>
                    </Col>
                </FormGroup>
                <Button color="primary"><AiOutlineForward /> Verified Data</Button>
            </Form>
        </div>
    );
}

export default EditStudent;
Enter fullscreen mode Exit fullscreen mode
  • list-student.component.jsx
import React, { useState, useEffect } from 'react';
import { Link } from 'react-router-dom';
import axios from 'axios';
import { Table, Badge } from 'reactstrap';
import { AiOutlineEdit, AiOutlineDelete } from 'react-icons/ai';

const ListBar = (props) => {
    return (
        <tr>
            <td>{props.student.student_name}</td>
            <td>{props.student.student_number}</td>
            <td>{props.student.student_entry}</td>
            <td>
                {props.student.student_verification ? <Badge color="primary">Verified</Badge> : <Badge color="warning">Not Verified</Badge>}
            </td>
            <td>
                <Link to={"/edit/" + props.student._id}><AiOutlineEdit /></Link>
                <Link to={"/delete/"+props.student._id}><AiOutlineDelete /></Link>
            </td>
        </tr>
    );
}

const ListStudent = () => {
    const [listData, setListData] = useState({ lists: [] });

    useEffect(() => {
        const fetchData = async () => {
            const result = await axios(
                'http://localhost:4000/all_student/'
            );
            setListData({ lists: result.data });
        };
        fetchData();
    }, []);

    return (
        <div>
            <h3>List Student</h3>
            <Table striped style={{ marginTop: 20 }}>
                <thead>
                    <tr>
                        <th>Student Name</th>
                        <th>Student Number</th>
                        <th>Student Entry</th>
                        <th>Verification</th>
                        <th>Action</th>
                    </tr>
                </thead>
                <tbody>
                    {listData.lists.map((current, i) => (
                        <ListBar student={current} key={i} />
                    ))}
                </tbody>
            </Table>
        </div>
    );
}

export default ListStudent;
Enter fullscreen mode Exit fullscreen mode
  • Reorganize your project 🎮

Modify index.html in public folder
Modify the title into School MERN App.

Import all components into App.js.
Add link, router, route from react-router-dom into App.js.

  • src/App.js
import React from 'react';
import "bootstrap/dist/css/bootstrap.min.css";
import './App.css';
import { BrowserRouter as Router, Route, Link } from 'react-router-dom';

import ListStudent from './components/list-student.component';
import EditStudent from './components/edit-student.component';
import CreateStudent from './components/create-student.component';
import DeleteStudent from './components/delete-student.component';

const App = () => {
  return (
    <Router>
      <div className="container">
        <nav className="navbar navbar-expand-lg navbar-dark bg-dark">
          <Link to="/" className="navbar-brand">School MERN App</Link>
          <div className="collapse navbar-collapse">
            <ul className="navbar-nav mr-auto">
              <li className="navbar-item">
                <Link to="/" className="nav-link">List Student</Link>
              </li>
              <li className="navbar-item">
                <Link to="/create" className="nav-link">Create Student</Link>
              </li>
            </ul>
          </div>
        </nav>
        <br />
        <Route path="/" exact component={ListStudent} />
        <Route path="/edit/:id" component={EditStudent} />
        <Route path="/create" component={CreateStudent} />
        <Route path="/delete/:id" component={DeleteStudent} />
      </div>
    </Router>
  );
}

export default App;
Enter fullscreen mode Exit fullscreen mode

Let's take a break ☕ ☕ ☕ before continue to backend!

Let's get started -- Backend ⚡

  • Make a new folder for backend

mkdir backend
cd backend
npm init -y

  • Begin with add backend/server.js as command
const express = require('express');
const app = express();
const bodyParser = require('body-parser');
const cors = require('cors');
const mongoose = require('mongoose');
const PORT = 4000;

const crudRoutes = express.Router();
let Crud = require('./crud.model');

app.use(cors());
app.use(bodyParser.json());

mongoose.connect('mongodb://localhost:27017/?readPreference=primary&appname=MongoDB%20Compass%20Community&ssl=false/crud_mern',
    { useNewUrlParser: true });
const connection = mongoose.connection;

connection.once('open', () => {
    console.log("MongoDB database connection established successfully");
})

crudRoutes.route('/').get((req, res) => {
    Crud.find((err, results) => {
        if (err) console.log(err);
        else res.json(results);
    });
});

crudRoutes.route('/:id').get((req, res) => {
    let id = req.params.id;
    Crud.findById(id, (err, result) => {
        if (err) console.log(err);
        else res.json(result);
    });
});

crudRoutes.route('/add').post((req, res) => {
    let list = new Crud(req.body);
    list.save().then(list => {
        res.status(200).json({'list': 'Student added successfully'});
    }).catch(err => {
        res.status(400).send('Adding failed');
    });
});

crudRoutes.route('/update/:id').post((req, res) => {
    Crud.findById(req.params.id, (err, data) => {
        if (!data) res.status(404).send("Student is not found");
        else {
            data.student_name = req.body.student_name;
            data.student_address = req.body.student_address;
            data.student_number = req.body.student_number;
            data.student_entry = req.body.student_entry;
            data.student_year = req.body.student_year;
            data.student_verification = req.body.student_verification;

            data.save().then(data => {
                res.json('Data student is updated!');
            }).catch(err => {
                res.status(400).send("Update isn't possible");
            });
        }
    });
});

crudRoutes.route('/delete/:id').delete((req, res) => {
    Crud.findByIdAndRemove(req.params.id, (err, data) => {
        if (err) return res.status(500).send("There was a problem deleting the user.");
        res.status(200).send(`Student ${data.student_name} was deleted`);
    })
})

app.use('/all_student', crudRoutes);

app.listen(PORT, () => {
    console.log("Server is running on PORT: " + PORT);
})
Enter fullscreen mode Exit fullscreen mode
  • Add backend/crud.model.js as a Schema for MongoDB
const mongoose = require('mongoose');
const Schema = mongoose.Schema;

let crudStudent = new Schema({
    student_name: {
        type: String
    },
    student_address: {
        type: String
    },
    student_number: {
        type: Number
    },
    student_entry: {
        type: String
    },
    student_year: {
        type: Number
    },
    student_verification: {
        type: Boolean
    }
});

module.exports = mongoose.model('school_student', crudStudent);
Enter fullscreen mode Exit fullscreen mode
  • Run backend folder with nodemon server .

Additional:

In backend folder, you will find node_modules, crud.model.js, package.json, package-lock.json, and server.js

Don't forget to install nodemon to run your backend

Final Part - Run the Application

You are finally implementing MERN with your simple create-read-update-delete (CRUD) student app. You can implement it for another project.

Additional Notes

Here are some tools that I use for this:

  • Visual Studio Code
  • Postman
  • MongoDB Compass Community
  • Mozilla Firefox

If there are question about anything, you can contact me always 👓 ❤️ ❤️

Top comments (3)

Collapse
 
hikmatcnn profile image
hkmt hikayat

thank you

Collapse
 
tomgrigory profile image
Tom George

great tut|| Learned many new thingys..

Collapse
 
yongchanghe profile image
Yongchang He

Thank you for the effort of sharing knowledge!