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:
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:
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
@Repositorypublic 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=localhostspring.data.mongodb.port=27017spring.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
@SpringBootApplicationpublic 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