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
Thank you for the effort of sharing knowledge!
great tut|| Learned many new thingys..