React JS + Node JS + Express + MongoDB CRUD - MERN stack development example

Hello everyone, today we will learn how to develop a MERN stack web application that is a basic User Management Application using MongoDB, Express, React JS, Node JS, and AdminLTE.GitHub repository link is provided at the end of this tutorial. You can download the source code. 


 

Following technologies stack being used:


Back-End:

  • Node JS
  • Express JS
  • Mongo DB
Front-End:
  • React JS
  • AdminLTE
  • react-router-dom 5+
  • axios 0.19.2
  • bootstrap
 
Project Structure 
Back-End:
 

 
Front-End:
 

After completing this tutorial what we will build?
 
1)We will build a MERN-stack web application that is a basic User Management 
Application with CRUD features: 


• Create User 
• List User 
• Update User 
• Delete User 
 AND
 
2)How to integrate Admin Template In ReactJS?
 
Following is the screenshot of our application -
 

MERN stack App development
 
We will build two projects: 
  1. Node JS+Express JS: Back End
  2. React JS+AdminLTE: Front End
 

Project 1:BackEnd

package.json

{
"name": "knf-BackEnd",
"version": "0.1.0",
"private": true,
"keywords": [
"Express",
"RestAPI",
"MongoDB",
"Mongoose",
"Notes"
],
"author": "Sibin",
"license": "ISC",
"dependencies": {
"body-parser": "^1.19.0",
"cors": "^2.8.5",
"express": "^4.17.1",
"mongoose": "^5.8.10"
}
}



server.js

const express = require('express');
const bodyParser = require('body-parser');
// create express app
const app = express();
// configure the app to use bodyParser()
app.use(bodyParser.urlencoded({
extended: true
}));
app.use(bodyParser.json());
var cors = require('cors')
app.use(cors())
// ... Your routes and methods here
// Configuring the database
const dbConfig = require('./config/database.config.js');
const mongoose = require('mongoose');
mongoose.Promise = global.Promise;
// Connecting to the database
mongoose.connect(dbConfig.url, {
useNewUrlParser: true
}).then(() => {
console.log("Successfully connected to the database");
}).catch(err => {
console.
log('Could not connect to the database. Exiting now...', err);
process.exit();
});
require('./app/routes/user.routes.js')(app);
// listen for requests
app.listen(8081, () => {
console.log("Server is listening on port 8081");
});


database.config.js


module.exports = {
url: 'mongodb://localhost:27017/taletapro-dev'

}



user.controller.js

const User = require('../models/user.model.js');
// Create and Save a new User
exports.create = (req, res) => {
// Create a User
const note = new User({
firstName: req.body.firstName,
lastName: req.body.lastName,
emailId: req.body.emailId
});
// Save User in the database
note.save()
.then(data => {
res.send(data);
}).catch(err => {
res.status(500).send({
message: err.message ||
"Some error occurred while creating the User."
});
});
};
// Retrieve and return all Users from the database.
exports.findAll = (req, res) => {
User.find()
.then(notes => {
res.send(notes);
}).catch(err => {
res.status(500).send({
message: err.message ||
"Some error occurred while retrieving users."
});
});
};
// Find a single User with a id
exports.findOne = (req, res) => {
User.findById(req.params.id)
.then(note => {
if (!note) {
return res.status(404).send({
message: "Note not found with id "
+ req.params.id
});
}
res.send(note);
}).catch(err => {
if (err.kind === 'ObjectId') {
return res.status(404).send({
message: "Note not found with id "
+ req.params.id
});
}
return res.status(500).send({
message: "Error retrieving note with id "
+ req.params.id
});
});
};
// Update a User identified by the id in the request
exports.update = (req, res) => {
User.findByIdAndUpdate(req.params.id, req.body, { new: true })
.then((user) => {
if (!user) {
return res.status(404).send({
message: "no user found",
});
}
res.status(200).send(user);
})
.catch((err) => {
return res.status(404).send({
message: "error while updating the post",
});
});
};
// Delete a User with the specified id in the request
exports.delete = (req, res) => {
User.findByIdAndRemove(req.params.id)
.then(note => {
// if(!note) {
//return res.status(404).send({
// message: "User not found with id "
// + req.params.id
//});
// }
res.send({ message: "User deleted successfully!" });
}).catch(err => {
if (err.kind === 'ObjectId' || err.name === 'NotFound') {
return res.status(404).send({
message: "User not found with id "
+ req.params.id
});
}
return res.status(500).send({
message: "Could not delete User with id "
+ req.params.id
});
});
};


user.models.js

const mongoose = require('mongoose');

const UserSchema = mongoose.Schema({
id: String,
firstName: String,
lastName: String,
emailId: String
}, {
timestamps: true
});
module.exports = mongoose.model('User', UserSchema);



user.routes.js

module.exports = (app) => {

const users = require('../controllers/user.controller.js');
app.post('/users', users.create);
app.get('/users', users.findAll);
app.get('/users/:id', users.findOne);
app.put('/users/:id', users.update);
app.delete('/users/:id', users.delete);
}


Project 2:FrontEnd

package.json

{
"name": "knf-FrontEnd",
"version": "0.1.0",
"private": true,
"dependencies": {
"@testing-library/jest-dom": "^4.2.4",
"@testing-library/react": "^9.3.2",
"@testing-library/user-event": "^7.1.2",
"react": "^16.14.0",
"react-dom": "^16.14.0",
"react-scripts": "3.4.3",
"axios": "^0.19.2",
"react-router-dom": "^5.2.0"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
"eslintConfig": {
"extends": "react-app"
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
}
}


App.js

import React, { Component } from 'react'
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom'
import Footer from './components/Footer'
import Header from './components/Header'
import Menu from './components/Menu'
import ListUserComponent from './components/ListUserComponent'
import CreateUserComponent from './components/CreateUserComponent'
import ViewUserComponent from './components/ViewUserComponent'
import UpdateUserComponent from './components/UpdateUserComponent'

export default class App extends Component {
render() {
return (
<div>
<Router>
<Header />
<Menu />
<Switch>
<Route path="/" exact component={ListUserComponent}></Route>
<Route path="/users" component={ListUserComponent}></Route>
<Route path="/add-user/:_id" component={CreateUserComponent}></Route>
<Route path="/view-user/:_id" component={ViewUserComponent}></Route>
<Route path="/update-user/:_id" component={UpdateUserComponent}></Route>
</Switch>
<Footer />
</Router>
</div>
)
}
}


CreateUserComponent.jsx

import React, { Component } from 'react'
import UserService from '../services/UserService';

class CreateUserComponent extends Component {
constructor(props) {
super(props)
this.state = {
// step 2
_id: this.props.match.params._id,
firstName: '',
lastName: '',
emailId: ''
}
this.changeFirstNameHandler = this.changeFirstNameHandler.bind(this);
this.changeLastNameHandler = this.changeLastNameHandler.bind(this);
this.saveOrUpdateUser = this.saveOrUpdateUser.bind(this);
}
// step 3
componentDidMount() {
// step 4
if (this.state._id === '_add') {
return
} else {
UserService.getUserById(this.state._id).then((res) => {
let user = res.data;
this.setState({
firstName: user.firstName,
lastName: user.lastName,
emailId: user.emailId
});
});
}
}
saveOrUpdateUser = (e) => {
e.preventDefault();
let user = { firstName: this.state.firstName,
lastName: this.state.lastName,
emailId: this.state.emailId };
console.log('user => ' + JSON.stringify(user));
// step 5
if (this.state._id === '_add') {
UserService.createUser(user).then(res => {
this.props.history.push('/users');
});
} else {
UserService.updateUser(user, this.state._id).then(res => {
this.props.history.push('/users');
});
}
}
changeFirstNameHandler = (event) => {
this.setState({ firstName: event.target.value });
}
changeLastNameHandler = (event) => {
this.setState({ lastName: event.target.value });
}
changeEmailHandler = (event) => {
this.setState({ emailId: event.target.value });
}
cancel() {
this.props.history.push('/users');
}
getTitle() {
if (this.state._id === '_add') {
return <h3 className="text-center">Add User</h3>
} else {
return <h3 className="text-center">Update User</h3>
}
}
render() {
return (
<div>
<br></br>
<div className="content-wrapper">
<div className="row">
<div className="card col-md-6 offset-md-3 offset-md-3">
{
this.getTitle()
}
<div className="card-body">
<form>
<div className="form-group">
<label> First Name: </label>
<input placeholder="First Name" name="firstName"
className="form-control"
value={this.state.firstName}
onChange={this.changeFirstNameHandler} />
</div>
<div className="form-group">
<label> Last Name: </label>
<input placeholder="Last Name" name="lastName"
className="form-control"
value={this.state.lastName}
onChange={this.changeLastNameHandler} />
</div>
<div className="form-group">
<label> Email Id: </label>
<input placeholder="Email Address" name="emailId"
className="form-control"
value={this.state.emailId}
onChange={this.changeEmailHandler} />
</div>
<button className="btn btn-success"
onClick={this.saveOrUpdateUser}>Save</button>
<button className="btn btn-danger"
onClick={this.cancel.bind(this)}
style={{ marginLeft: "10px" }}>Cancel</button>
</form>
</div>
</div>
</div>
</div>
</div>
)
}
}
export default CreateUserComponent


ListUserComponent.jsx

import React, { Component } from 'react'
import UserService from '../services/UserService';

class ListUserComponent extends Component {
constructor(props) {
super(props)
this.state = {
users: []
}
this.addUser = this.addUser.bind(this);
this.editUser = this.editUser.bind(this);
this.deleteUser = this.deleteUser.bind(this);
}
deleteUser(_id) {
UserService.deleteUser(_id).then(res => {
this.setState({ users: this.state.users.
filter(user => user._id !== _id) });
});
}
viewUser(_id) {
this.props.history.push(`/view-user/${_id}`);
}
editUser(_id) {
this.props.history.push(`/add-user/${_id}`);
}
componentDidMount() {
UserService.getUsers().then((res) => {
this.setState({ users: res.data });
});
}
addUser() {
this.props.history.push('/add-user/_add');
}
render() {
return (
<div>
<div className="content-wrapper">
<h2 className="text-center">Users List</h2>
<div className="row">
<button className="btn btn-primary"
onClick={this.addUser}> Add User</button>
</div>
<br></br>
<div className="row">
<table className="table table-striped table-bordered">
<thead>
<tr>
<th> User First Name</th>
<th> User Last Name</th>
<th> User Email Id</th>
<th> Actions</th>
</tr>
</thead>
<tbody>
{
this.state.users.map(
user =>
<tr key={user._id}>
<td> {user.firstName} </td>
<td> {user.lastName}</td>
<td> {user.emailId}</td>
<td>
<button onClick={() => this.editUser(user._id)}
className="btn btn-info">Update </button>
<button style={{ marginLeft: "10px" }}
onClick={() => this.deleteUser(user._id)}
className="btn btn-danger">Delete </button>
<button style={{ marginLeft: "10px" }}
onClick={() => this.viewUser(user._id)}
className="btn btn-info">View </button>
</td>
</tr>)
}</tbody>
</table>
</div>
</div>
</div>
)
}
}
export default ListUserComponent


UpdateUserComponent.jsx

import React, { Component } from 'react'

import UserService from '../services/UserService';

class UpdateUserComponent extends Component {
constructor(props) {
super(props)
this.state = {
_id: this.props.match.params._id,
firstName: '',
lastName: '',
emailId: ''
}
this.changeFirstNameHandler = this.changeFirstNameHandler.bind(this);
this.changeLastNameHandler = this.changeLastNameHandler.bind(this);
this.updateUser = this.updateUser.bind(this);
}
componentDidMount() {
UserService.getUserById(this.state._id).then((res) => {
let user = res.data;
this.setState({
firstName: user.firstName,
lastName: user.lastName,
emailId: user.emailId
});
});
}
updateUser = (e) => {
e.preventDefault();
let user = { firstName: this.state.firstName,
lastName: this.state.lastName, emailId: this.state.emailId };
console.log('user => ' + JSON.stringify(user));
console.log('_id => ' + JSON.stringify(this.state._id));
UserService.updateUser(user, this.state._id).then(res => {
this.props.history.push('/users');
});
}
changeFirstNameHandler = (event) => {
this.setState({ firstName: event.target.value });
}
changeLastNameHandler = (event) => {
this.setState({ lastName: event.target.value });
}
changeEmailHandler = (event) => {
this.setState({ emailId: event.target.value });
}
cancel() {
this.props.history.push('/users');
}
render() {
return (
<div>
<br></br>
<div className="content-wrapper">
<div className="row">
<div className="card col-md-6 offset-md-3 offset-md-3">
<h3 className="text-center">Update User</h3>
<div className="card-body">
<form>
<div className="form-group">
<label> First Name: </label>
<input placeholder="First Name"
name="firstName" className="form-control"
value={this.state.firstName}
onChange={this.changeFirstNameHandler} />
</div>
<div className="form-group">
<label> Last Name: </label>
<input placeholder="Last Name"
name="lastName" className="form-control"
value={this.state.lastName}
onChange={this.changeLastNameHandler} />
</div>
<div className="form-group">
<label> Email Id: </label>
<input placeholder="Email Address"
name="emailId" className="form-control"
value={this.state.emailId}
onChange={this.changeEmailHandler}/>
</div>
<button className="btn btn-success"
onClick={this.updateUser}>Save</button>
<button className="btn btn-danger"
onClick={this.cancel.bind(this)} style={{
marginLeft: "10px" }}>Cancel</button>
</form>
</div>
</div>
</div>
</div>
</div>
)
}
}
export default UpdateUserComponent



ViewUserComponent.jsx

import React, { Component } from 'react'

import UserService from '../services/UserService';

class ViewUserComponent extends Component {
constructor(props) {
super(props)
this.state = {
_id: this.props.match.params._id,
user: {}
}
}
componentDidMount() {
UserService.getUserById(this.state._id).then(res => {
this.setState({ user: res.data });
})
}
render() {
return (
<div>
<br></br><div className="content-wrapper">
<div className="card col-md-6 offset-md-3">
<h3 className="text-center"> View User Details</h3>
<div className="card-body">
<div className="row">
<label> User First Name: </label>
<div> {this.state.user.firstName}</div>
</div>
<div className="row">
<label> User Last Name: </label>
<div> {this.state.user.lastName}</div>
</div>
<div className="row">
<label> User Email ID: </label>
<div> {this.state.user.emailId}</div>
</div>
</div>
</div>
</div></div>
)
}
}
export default ViewUserComponent



UserService.js
import axios from 'axios';
const USER_API_BASE_URL = "http://localhost:8081/users";

class UserService {
getUsers() {
return axios.get(USER_API_BASE_URL);
}
createUser(user) {
return axios.post(USER_API_BASE_URL, user);
}
getUserById(userId) {
return axios.get(USER_API_BASE_URL + '/' + userId);
}
updateUser(user, userId) {
return axios.put(USER_API_BASE_URL + '/' + userId, user);
}
deleteUser(userId) {
return axios.delete(USER_API_BASE_URL + '/' + userId);
}
}
export default new UserService()


Local Environment Setup:


Then, npm install installs all modules that are listed on the package.json file and their dependencies


npm install
npm start

Execute the above commands for both Frontend and Backend.


More related topics,

Popular posts from this blog

Learn Java 8 streams with an example - print odd/even numbers from Array and List

Java Stream API - How to convert List of objects to another List of objects using Java streams?

Registration and Login with Spring Boot + Spring Security + Thymeleaf

Java, Spring Boot Mini Project - Library Management System - Download

ReactJS, Spring Boot JWT Authentication Example

Spring Boot + Mockito simple application with 100% code coverage

Top 5 Java ORM tools - 2024

Java - Blowfish Encryption and decryption Example

Spring boot video streaming example-HTML5

Google Cloud Storage + Spring Boot - File Upload, Download, and Delete