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

Technology:

   Back-End:

  • Java 1.8
  • Spring Boot 2+ 
  • Maven 3.2+
  • H2DB       

   Front-End:

  • React
  • react-router-dom 5+   
  • axios 0.19.2
  • bootstrap 4.4.1

Spring Boot +React JS CRUD Full Stack Architecture


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
    • View User

Following is the screenshot of our application -


A quick overview of React and Spring Boot
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-jpa 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</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>demo</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-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</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(UserController)


package com.knf.sibin.dev.controller;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
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.exception.ResourceNotFoundException;
import com.knf.sibin.dev.model.User;
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) {
return userRepository.save(user);
}

// get user by id rest api
@GetMapping("/users/{id}")
public ResponseEntity<User> getUserById(@PathVariable Long 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 Long 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 Long 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(UserRepository)


package com.knf.sibin.dev.repository;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import com.knf.sibin.dev.model.User;

@Repository
public interface UserRepository extends JpaRepository<User, Long> {

}


Model(User)


package com.knf.sibin.dev.model;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name = "user")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
@Column(name = "first_name")
private String firstName;
@Column(name = "last_name")
private String lastName;
@Column(name = "email_id")
private String emailId;

public User() {
}

public User(String firstName, String lastName, String emailId) {
super();
this.firstName = firstName;
this.lastName = lastName;
this.emailId = emailId;
}

public long getId() {
return id;
}

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

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;
}
}

Run

$ mvn spring-boot:run



Project 2:React App, Front-End

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.json

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: 

https://github.com/knowledgefactory4u/springboot-react-crud-client.git

Comments