React JS + Spring Boot + MongoDB CRUD example

Hello everyone, today we will learn how to develop a full-stack web application that is a basic User Management Application using React, Spring Boot, and MongoDB.You could download the source code from our Github repository, the download link is provided at the end of this tutorial.


Technologies used: 


Back-End: 

  • Java 17
  • Spring Boot 2.7.0
  • Spring Boot Data Mongo
  • Mongo DB.

Front-End: 

  • React 17.0.1
  • Axios 0.27.2
  • Bootstrap 4.5.0

Backend Project Directory:



Frontend Project Directory:




After completing this tutorial what we will build? 

We will build a full-stack web application that is a basic User Management Application with CRUD features: 

• Create User 
• List User 
• Update User 
• Delete User  


List User:


Add User:


Update User:


View User:



We will build two projects: 

1. springboot-mongodb-crud – Backend
2. react-crud  – Frontend


Project 1:springboot-mongodb-crud

pom.xml

Include spring-boot-starter-web for Spring MVC and REST structure, spring-boot-starter-data-mongodb for CRUD repository.
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.0</version>
<relativePath />
</parent>
<groupId>com.knf.sibin.dev</groupId>
<artifactId>springboot-mongodb-crud</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>springboot-mongodb-crud</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>17</java.version>
</properties>

<dependencies>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>


Document

@Document(collection = "users")
public class User {

@Id
private String id;
private String firstName;
private String lastName;
private String emailId;
public User() {

}

public User(String firstName,
String lastName, String emailId) {

super();
this.firstName = firstName;
this.lastName = lastName;
this.emailId = emailId;
}

public String getFirstName() {
return firstName;
}

public void setFirstName(String firstName) {
this.firstName = firstName;
}

public String getLastName() {
return lastName;
}

public void setLastName(String lastName) {
this.lastName = lastName;
}

public String getEmailId() {
return emailId;
}

public void setEmailId(String emailId) {
this.emailId = emailId;
}

public String getId() {
return id;
}

public void setId(String id) {
this.id = id;
}
}


Repository 

@Repository
public interface UserRepository
extends MongoRepository<User, String> {

}



ResourceNotFoundException

@ResponseStatus(value = HttpStatus.NOT_FOUND)
public class ResourceNotFoundException extends RuntimeException{

private static final long serialVersionUID = 1L;
public ResourceNotFoundException(String message) {
super(message);
}
}



application.properties

spring.data.mongodb.host=localhost
spring.data.mongodb.port=27017
spring.data.mongodb.database=userdb 


RestController 

@CrossOrigin(origins = "*", maxAge = 4800)
@RestController
@RequestMapping("/api/v1")
public class UserController {
@Autowired
private UserRepository userRepository;

@GetMapping("/users")
public List<User> getAllUsers() {
return userRepository.findAll();
}

@PostMapping("/users")
public User createUser(@RequestBody User user) {
Random random = new Random();
user.setId((user.getFirstName() + user.getLastName()
+ user.getEmailId()) + random.nextInt(1000));

return userRepository.save(user);
}

@GetMapping("/users/{id}")
public ResponseEntity<User> getUserById
(@PathVariable String id) {
User user = userRepository.findById(id)
.orElseThrow(() -> new ResourceNotFoundException
("User not exist with id :" + id));

return ResponseEntity.ok(user);
}

@PutMapping("/users/{id}")
public ResponseEntity<User> updateUser
(@PathVariable String id, @RequestBody User userDetails) {
User user = userRepository.findById(id)
.orElseThrow(() -> new ResourceNotFoundException
("User not exist with id :" + id));

user.setFirstName(userDetails.getFirstName());
user.setLastName(userDetails.getLastName());
user.setEmailId(userDetails.getEmailId());
User updatedUser = userRepository.save(user);

return ResponseEntity.ok(updatedUser);
}

@DeleteMapping("/users/{id}")
public ResponseEntity<Map<String, Boolean>> deleteUser
(@PathVariable String id) {
User user = userRepository.findById(id)
.orElseThrow(() -> new ResourceNotFoundException
("User not exist with id :" + id));

userRepository.delete(user);
Map<String, Boolean> response = new HashMap<>();
response.put("deleted", Boolean.TRUE);

return ResponseEntity.ok(response);
}
}


Main Driver

@SpringBootApplication
public class MainApplication {

public static void main(String[] args) {
SpringApplication.run(MainApplication.class, args);
}
}




Project 2: react-crud

package.json
{
"name": "react-crud",
"version": "0.1.0",
"private": true,
"dependencies": {
"@testing-library/jest-dom": "^4.2.4",
"@testing-library/react": "^9.5.0",
"@testing-library/user-event": "^7.2.1",
"axios": "^0.27.2",
"bootstrap": "^4.5.0",
"react": "^17.0.1",
"react-dom": "^17.0.1",
"react-router-dom": "^5.2.0",
"react-scripts": "3.4.3"
},
"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"
]
}
}


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="container">
<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) => {
if(res.data==null)
{
this.props.history.push('/add-user/_add');
}
this.setState({ users: res.data});
});
}

addUser(){
this.props.history.push('/add-user/_add');
}

render() {
return (
<div>
<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>
)
}
}

export default ListUserComponent


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 = "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>
)
}
}

export default ViewUserComponent



UserService.js

import axios from 'axios';

const USER_API_BASE_URL = "http://localhost:8080/api/v1/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()



App.css

.App {
text-align: center;
}

.App-logo {
height: 40vmin;
pointer-events: none;
}

@media (prefers-reduced-motion: no-preference) {
.App-logo {
animation: App-logo-spin infinite 20s linear;
}
}

.App-header {
background-color: #282c34;
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
font-size: calc(10px + 2vmin);
color: white;
}

.App-link {
color: #61dafb;
}

@keyframes App-logo-spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}

.footer {
position: absolute;
bottom: 0;
width:100%;
height: 50px;
background-color: black;
text-align: center;
color: white;
}



App.js

import React from 'react';
import './App.css';
import {BrowserRouter as Router, Route, Switch} from 'react-router-dom'
import ListUserComponent from './components/ListUserComponent';
import HeaderComponent from './components/HeaderComponent';
import FooterComponent from './components/FooterComponent';
import CreateUserComponent from './components/CreateUserComponent';
import ViewUserComponent from './components/ViewUserComponent';

function App() {
return (
<div>
<Router>
<HeaderComponent />
<div className="container">
<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>
</Switch>
</div>
<FooterComponent />
</Router>
</div>
);
}

export default App;



index.css

body {
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI',
'Roboto', 'Oxygen',
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans',
'Helvetica Neue',
sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}

code {
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
monospace;
}



index.js

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';
import 'bootstrap/dist/css/bootstrap.min.css';

ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById('root')
);

serviceWorker.unregister();



Download the complete source code - click here

                                        

Local Setup and Run the application

Step 1: Install Mongo DB - Click here


Step 2: Download or clone the source code from GitHub to a local machine - Click here



Backend


Step 3: mvn clean install


Step 4: Run the Spring Boot application - mvn spring-boot:run



Frontend


Step 5: npm install 


Step 6: npm start

From the browser call the endpoint http://localhost:3000/

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

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