React JS + Spring Boot + MongoDB CRUD - Full stack development foundation

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.GitHub repository link is provided at the end of this tutorial. You can download the source code.


Technology: 


Back-End:

Front-End:

  • React-router-dom 5+
  • Axios 0.19.2
  • bootstrap 4.4.1
Project Structure of Back-End

Project Structure of Front-End:

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  

Following is the screenshot of our application -

A quick overview of React, Spring Boot, and MongoDB Atlas
What and Why MongoDB Atlas?
MongoDB Atlas is MongoDB’s managed MongoDB as a Service. That means that Atlas takes the responsibility of hosting, patching, managing, and securing your MongoDB cluster, and leaves you free for putting it to good use.
MongoDB Atlas takes the responsibility of doing the boring stuff of running MongoDB clusters away and leaves you to just use the fun stuff and not worry about your data or apps status. This means that most people who use MongoDB should be using MongoDB Atlas.
What and Why React.js?
React.js is an open-source JavaScript library that is used for building user interfaces specifically for single-page applications. React allows developers to create large web applications that can change data, without reloading the page. The main purpose of React is to be fast, scalable, and simple. It works only on user interfaces in the application.
What and Why Spring Boot?
Spring boot to develop REST web services and microservices. Spring Boot has taken the Spring framework to the next level. It has drastically reduced the configuration and setup time required for spring projects. We can set up a project with almost zero configuration and start building the things that actually matter to your application.
Full-Stack App Development
We will build two projects: 
1. sprint boot-backend – To develop REST API 
2. react-frontend  – Consume REST API
Project 1:Spring Boot-CRUD-Restful-API
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.1.1.RELEASE</version>
<relativePath /> <!-- lookup parent from repository -->
</parent>
<groupId>com.knf.sibin.dev</groupId>
<artifactId>springboot_crud_mongo_react</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>springboot_crud_mongo_react</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</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.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</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>


RestController


package com.knf.sibin.dev.controller;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Random;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.knf.sibin.dev.document.User;
import com.knf.sibin.dev.exception.ResourceNotFoundException;
import com.knf.sibin.dev.repository.UserRepository;

@CrossOrigin(origins = "http://localhost:3000")
@RestController
@RequestMapping("/api/v1/")
public class UserController {
@Autowired
private UserRepository userRepository;

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

// create user rest api
@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);
}

// get user by id rest api
@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);
}

// update user rest api
@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);
}

// delete user rest api
@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);
}
}

Repository


package com.knf.sibin.dev.repository;

import org.springframework.data.mongodb.repository.MongoRepository;
import org.springframework.stereotype.Repository;
import com.knf.sibin.dev.document.User;

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

Document


package com.knf.sibin.dev.document;

import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.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;
}
}

package.json

{

"name""react-frontend",

"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.19.2",

"bootstrap""^4.5.0",

"react""^16.13.1",

"react-dom""^16.13.1",

"react-router-dom""^5.2.0",

"react-scripts""3.4.1"

},

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

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


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 = "container">

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



Run

npm start



Download:

Thank You,Sibin 


Comments