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
 
yongchanghe profile image
Yongchang He

Thank you for the effort of sharing knowledge!

Collapse
 
tomgrigory profile image
Tom George

great tut|| Learned many new thingys..