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
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
Make sure Node.JS is installed.
Node.JS: https://nodejs.org/en/download/Install React App with NPX.
npx create-react-app school-app
- 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
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;
- 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;
- 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;
- 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;
- 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;
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); })
- 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);
- 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)
thank you
great tut|| Learned many new thingys..
Thank you for the effort of sharing knowledge!