Code Example on GitHub
- Create Employee
- List Employee
- Update Employee
- Delete Employee
Quick overview of React and Spring Boot
What is React JS?
- React is used to build user interfaces (UI) on the front end.
- React is not a framework (unlike Angular, which is more opinionated).
- React is an open-source project created by Facebook.
What are React Hooks?
Hooks were added to React in version 16.8.Hooks allow function components to have access to state and other React features. Because of this, class components are generally no longer needed.
Hooks allow us to "hook" into React features such as state and lifecycle methods.
What is 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.
- You can set up a project with almost zero configuration and start building the things that actually matter to your application.
Prerequisites
- Basic familiarity with HTML & CSS
- Basic knowledge of JavaScript and programming
- Spring Boot Basics
- ReactJS basics
- Node.js and npm are installed globally
Full-Stack App Development
- sprint boot-backend (server) – To develop REST API
- react-hooks-frontend (client) – Consume REST API
Tools and Technologies Used
Server-side tools and technologies used
- Spring Boot 2 +
- SpringData JPA ( Hibernate)
- Maven 3.2 +
- JDK 1.8
- Embedded Tomcat 8.5+
- MySQL Database
Client-side tools and technologies used
- React
- React Hooks
- Modern JavaScript (ES6)
- NodeJS and NPM
- VS Code IDE
- Create React App CLI
- Bootstrap 5 and Axios HTTP Library
Spring Boot REST APIs Backend Development with MySQL Database
Let's create Spring Boot CRUD REST APIs project step by step and we will test CRUD REST APIs using the Postman client.
Let's create Spring Boot CRUD REST APIs project step by step and we will test CRUD REST APIs using the Postman client.
1. Create a Spring boot application
Spring Boot provides a web tool called Spring Initializer to bootstrap an application quickly. Just go to https://start.spring.io/ and generate a new spring boot project.
Use the below details in the Spring boot creation:
Project Name: springboot-backend
Project Type: Maven
Choose dependencies: Spring Web, Lombok, Spring Data JPA, and MySQL Driver
Package name: net.javaguides.springboot
Packaging: Jar
Download the Spring Boot project as a zip file, unzip it, and import it into IntelliJ IDEA.
Here is the pom.xml file for your reference:
<?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>3.0.4</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>net.javaguides</groupId>
<artifactId>springboot-backend</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>springboot-backend</name>
<description>Demo project for Spring Boot REST APIs</description>
<properties>
<java.version>17</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.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</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>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>
Spring Boot provides a web tool called Spring Initializer to bootstrap an application quickly. Just go to https://start.spring.io/ and generate a new spring boot project.
Use the below details in the Spring boot creation:
Project Name: springboot-backend
Project Type: Maven
Choose dependencies: Spring Web, Lombok, Spring Data JPA, and MySQL Driver
Package name: net.javaguides.springboot
Packaging: Jar
Download the Spring Boot project as a zip file, unzip it, and import it into IntelliJ IDEA.
Here is the pom.xml file for your reference:
<?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>3.0.4</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>net.javaguides</groupId>
<artifactId>springboot-backend</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>springboot-backend</name>
<description>Demo project for Spring Boot REST APIs</description>
<properties>
<java.version>17</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.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</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>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>
2. Create a Project or Packaging Structure
3. Configure MySQL Database
Since we’re using MySQL as our database, we need to configure the database URL, username, and password so that Spring can establish a connection with the database on startup. Open the src/main/resources/application.properties file and add the following properties to it:spring.datasource.url=jdbc:mysql://localhost:3306/ems?useSSL=false
spring.datasource.username=root
spring.datasource.password=Mysql@123
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQL5InnoDBDialect
spring.jpa.hibernate.ddl-auto = update
Don’t forget to change the spring.datasource.username and spring.datasource.password as per your MySQL installation. Also, create a database named ems in MySQL before proceeding to the next section.
You don’t need to create any tables. The tables will automatically be created by Hibernate from the Employee entity that we will define in the next step. This is made possible by the property spring.jpa.hibernate.ddl-auto = update.
spring.datasource.url=jdbc:mysql://localhost:3306/ems?useSSL=false
spring.datasource.username=root
spring.datasource.password=Mysql@123
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQL5InnoDBDialect
spring.jpa.hibernate.ddl-auto = update
4. Create JPA Entity
Go to the model package, create a class named Employee, and add the following content to it:package net.javaguides.springboot.model;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import jakarta.persistence.*;
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Entity
@Table(name = "employees")
public class Employee {
@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;
}
package net.javaguides.springboot.model;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import jakarta.persistence.*;
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Entity
@Table(name = "employees")
public class Employee {
@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;
}
5. Create Spring Data JPA Repository
No, we gonna create a Spring Data JPA repository to talk with the MySQL database.Go to the repository package, create the following EmployeeRepository interface, and add the following content to it:package net.javaguides.springboot.repository;
import net.javaguides.springboot.model.Employee;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
public interface EmployeeRepository extends JpaRepository<Employee, Long> {
// all crud database methods
}
package net.javaguides.springboot.repository;
import net.javaguides.springboot.model.Employee;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
public interface EmployeeRepository extends JpaRepository<Employee, Long> {
// all crud database methods
}
6. Create ResourceNotFoundException Custom Exception
Go to an exception package, create a class named ResourceNotFoundException, and add the following content to it:
package net.javaguides.springboot.exception;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;
@ResponseStatus(value = HttpStatus.NOT_FOUND)
public class ResourceNotFoundException extends RuntimeException{
public ResourceNotFoundException(String message){
super(message);
}
}
Go to an exception package, create a class named ResourceNotFoundException, and add the following content to it:
package net.javaguides.springboot.exception;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;
@ResponseStatus(value = HttpStatus.NOT_FOUND)
public class ResourceNotFoundException extends RuntimeException{
public ResourceNotFoundException(String message){
super(message);
}
}
7. Creating Spring Boot CRUD REST APIs
Go to the controller package, create a class named EmployeeController, and add the following content to it:
package net.javaguides.springboot.controller;
import net.javaguides.springboot.exception.ResourceNotFoundException;
import net.javaguides.springboot.model.Employee;
import net.javaguides.springboot.repository.EmployeeRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@CrossOrigin("*")
@RestController
@RequestMapping("/api/v1/employees")
public class EmployeeController {
@Autowired
private EmployeeRepository employeeRepository;
@GetMapping
public List<Employee> getAllEmployees(){
return employeeRepository.findAll();
}
// build create employee REST API
@PostMapping
public Employee createEmployee(@RequestBody Employee employee) {
return employeeRepository.save(employee);
}
// build get employee by id REST API
@GetMapping("{id}")
public ResponseEntity<Employee> getEmployeeById(@PathVariable long id){
Employee employee = employeeRepository.findById(id)
.orElseThrow(() -> new ResourceNotFoundException("Employee not exist with id:" + id));
return ResponseEntity.ok(employee);
}
// build update employee REST API
@PutMapping("{id}")
public ResponseEntity<Employee> updateEmployee(@PathVariable long id,@RequestBody Employee employeeDetails) {
Employee updateEmployee = employeeRepository.findById(id)
.orElseThrow(() -> new ResourceNotFoundException("Employee not exist with id: " + id));
updateEmployee.setFirstName(employeeDetails.getFirstName());
updateEmployee.setLastName(employeeDetails.getLastName());
updateEmployee.setEmailId(employeeDetails.getEmailId());
employeeRepository.save(updateEmployee);
return ResponseEntity.ok(updateEmployee);
}
// build delete employee REST API
@DeleteMapping("{id}")
public ResponseEntity<HttpStatus> deleteEmployee(@PathVariable long id){
Employee employee = employeeRepository.findById(id)
.orElseThrow(() -> new ResourceNotFoundException("Employee not exist with id: " + id));
employeeRepository.delete(employee);
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
}
}
Get All Employees REST API: @GetMapping
public List<Employee> getAllEmployees(){
return employeeRepository.findAll();
}
Create Employee REST API:
// build create employee REST API
@PostMapping
public Employee createEmployee(@RequestBody Employee employee) {
return employeeRepository.save(employee);
}
Get Employee by Id REST API:
// build get employee by id REST API
@GetMapping("{id}")
public ResponseEntity<Employee> getEmployeeById(@PathVariable long id){
Employee employee = employeeRepository.findById(id)
.orElseThrow(() -> new ResourceNotFoundException("Employee not exist with id:" + id));
return ResponseEntity.ok(employee);
}
Update Employee REST API:
// build update employee REST API
@PutMapping("{id}")
public ResponseEntity<Employee> updateEmployee(@PathVariable long id,@RequestBody Employee employeeDetails) {
Employee updateEmployee = employeeRepository.findById(id)
.orElseThrow(() -> new ResourceNotFoundException("Employee not exist with id: " + id));
updateEmployee.setFirstName(employeeDetails.getFirstName());
updateEmployee.setLastName(employeeDetails.getLastName());
updateEmployee.setEmailId(employeeDetails.getEmailId());
employeeRepository.save(updateEmployee);
return ResponseEntity.ok(updateEmployee);
}
Delete Employee REST API:
// build delete employee REST API
@DeleteMapping("{id}")
public ResponseEntity<HttpStatus> deleteEmployee(@PathVariable long id){
Employee employee = employeeRepository.findById(id)
.orElseThrow(() -> new ResourceNotFoundException("Employee not exist with id: " + id));
employeeRepository.delete(employee);
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
}
Go to the controller package, create a class named EmployeeController, and add the following content to it:
package net.javaguides.springboot.controller;
import net.javaguides.springboot.exception.ResourceNotFoundException;
import net.javaguides.springboot.model.Employee;
import net.javaguides.springboot.repository.EmployeeRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@CrossOrigin("*")
@RestController
@RequestMapping("/api/v1/employees")
public class EmployeeController {
@Autowired
private EmployeeRepository employeeRepository;
@GetMapping
public List<Employee> getAllEmployees(){
return employeeRepository.findAll();
}
// build create employee REST API
@PostMapping
public Employee createEmployee(@RequestBody Employee employee) {
return employeeRepository.save(employee);
}
// build get employee by id REST API
@GetMapping("{id}")
public ResponseEntity<Employee> getEmployeeById(@PathVariable long id){
Employee employee = employeeRepository.findById(id)
.orElseThrow(() -> new ResourceNotFoundException("Employee not exist with id:" + id));
return ResponseEntity.ok(employee);
}
// build update employee REST API
@PutMapping("{id}")
public ResponseEntity<Employee> updateEmployee(@PathVariable long id,@RequestBody Employee employeeDetails) {
Employee updateEmployee = employeeRepository.findById(id)
.orElseThrow(() -> new ResourceNotFoundException("Employee not exist with id: " + id));
updateEmployee.setFirstName(employeeDetails.getFirstName());
updateEmployee.setLastName(employeeDetails.getLastName());
updateEmployee.setEmailId(employeeDetails.getEmailId());
employeeRepository.save(updateEmployee);
return ResponseEntity.ok(updateEmployee);
}
// build delete employee REST API
@DeleteMapping("{id}")
public ResponseEntity<HttpStatus> deleteEmployee(@PathVariable long id){
Employee employee = employeeRepository.findById(id)
.orElseThrow(() -> new ResourceNotFoundException("Employee not exist with id: " + id));
employeeRepository.delete(employee);
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
}
}
Get All Employees REST API: @GetMapping
public List<Employee> getAllEmployees(){
return employeeRepository.findAll();
}
Create Employee REST API:
// build create employee REST API
@PostMapping
public Employee createEmployee(@RequestBody Employee employee) {
return employeeRepository.save(employee);
}
Get Employee by Id REST API:
// build get employee by id REST API
@GetMapping("{id}")
public ResponseEntity<Employee> getEmployeeById(@PathVariable long id){
Employee employee = employeeRepository.findById(id)
.orElseThrow(() -> new ResourceNotFoundException("Employee not exist with id:" + id));
return ResponseEntity.ok(employee);
}
Update Employee REST API:
// build update employee REST API
@PutMapping("{id}")
public ResponseEntity<Employee> updateEmployee(@PathVariable long id,@RequestBody Employee employeeDetails) {
Employee updateEmployee = employeeRepository.findById(id)
.orElseThrow(() -> new ResourceNotFoundException("Employee not exist with id: " + id));
updateEmployee.setFirstName(employeeDetails.getFirstName());
updateEmployee.setLastName(employeeDetails.getLastName());
updateEmployee.setEmailId(employeeDetails.getEmailId());
employeeRepository.save(updateEmployee);
return ResponseEntity.ok(updateEmployee);
}
Delete Employee REST API:
// build delete employee REST API
@DeleteMapping("{id}")
public ResponseEntity<HttpStatus> deleteEmployee(@PathVariable long id){
Employee employee = employeeRepository.findById(id)
.orElseThrow(() -> new ResourceNotFoundException("Employee not exist with id: " + id));
employeeRepository.delete(employee);
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
}
8. Running the Application
We have successfully developed all the CRUD Rest APIs for the Employee model. Now it's time to deploy our application in a servlet container(embedded tomcat). Two ways we can start the standalone Spring boot application. 1. From the root directory of the application and type the following command to run it -$ mvn spring-boot:run
2. From your IDE, run the SpringbootBackendApplication.main() method as a standalone Java class that will start the embedded Tomcat server on port 8080 and point the browser to http://localhost:8080/.
$ mvn spring-boot:run
9. Testing CRUD REST APIs using Postman
Test Get All Employees REST API:
Test Create Employee REST API:
Test Get Employee By Id REST API:
Test Update Employee REST API:
Test Delete Employee REST API:
React (React Hooks) Frontend Development
Let's go ahead and create a React application to consume CRUD REST APIs.I suggest you watch a YouTube video tutorial series to understand more about this full-stack app at React Hooks + Spring Boot CRUD Full Stack ApplicationAs we know, React is a JavaScript-based library that does not have the ability to make HTTP requests; thus, we need to use third-party libraries to achieve this.There are plenty of libraries available to make HTTP calls into React app. A few of them are listed below.- Axios
- Fetch
- Superagent
- React-axios
- Use-http
- React-request
We will use the Axios HTTP library to make HTTP Get REST API call in this tutorial.Let's start with creating a React App using create-react-app CLI.
- Axios
- Fetch
- Superagent
- React-axios
- Use-http
- React-request
1. Create a React UI with Create React App
The Create React App CLI tool is an officially supported way to create single-page React applications. It offers a modern build setup with no configuration.To create a new app, you may choose one of the following methods:
Using npx
npx create-react-app react-hooks-frontend
npx create-react-app react-hooks-frontend
Using npm
npm init react-app react-hooks-frontend
npm init is available in npm 6+
npm init react-app react-hooks-frontend
Using Yarn
yarn create react-app react-hooks-frontend
Running any of these commands will create a directory called react-hooks-frontend inside the current folder. Inside that directory, it will generate the initial project structure and install the transitive dependencies:react-hooks-frontend
├── README.md
├── node_modules
├── package.json
├── .gitignore
├── public
│ ├── favicon.ico
│ ├── index.html
│ ├── logo192.png
│ ├── logo512.png
│ ├── manifest.json
│ └── robots.txt
└── src
├── App.css
├── App.js
├── App.test.js
├── index.css
├── index.js
├── logo.svg
└── serviceWorker.js
Let's explore important files and folders of the React project.For the project to build, these files must exist with exact filenames:- public/index.html is the page template;
- src/index.js is the JavaScript entry point.
You can delete or rename the other files Let's quickly explore the project structure.
package.json - The package.json file contains all the required dependencies for our React JS project. Most importantly, you can check the current version of React that you are using. It has all the scripts to start, build, and eject our React app.public folder - The public folder contains index.html. As React is used to build a single-page application, we have this single HTML file to render all our components. Basically, it's an HTML template. It has a div element with id as root and all our components are rendered in this div with index.html as a single page for the complete react app.src folder- In this folder, we have all the global javascript and CSS files. All the different components that we will be building, sit here.index.js - This is the top renderer of your React app. node_modules - All the packages installed by NPM or Yarn will reside inside the node_modules folder.App.js - The App.js file contains the definition of our App component which actually gets rendered in the browser and this is the root component.
yarn create react-app react-hooks-frontend
Running any of these commands will create a directory called react-hooks-frontend inside the current folder. Inside that directory, it will generate the initial project structure and install the transitive dependencies:react-hooks-frontend
├── README.md
├── node_modules
├── package.json
├── .gitignore
├── public
│ ├── favicon.ico
│ ├── index.html
│ ├── logo192.png
│ ├── logo512.png
│ ├── manifest.json
│ └── robots.txt
└── src
├── App.css
├── App.js
├── App.test.js
├── index.css
├── index.js
├── logo.svg
└── serviceWorker.js
- public/index.html is the page template;
- src/index.js is the JavaScript entry point.
2. Adding Bootstrap in React Using NPM
Open a new terminal window, navigate to your project's folder, and run the following command:$ npm install bootstrap --save
After installing the bootstrap package, you will need to import it into your React app entry file.Open the src/index.js file and add the following code:import 'bootstrap/dist/css/bootstrap.min.css';
$ npm install bootstrap --save
import 'bootstrap/dist/css/bootstrap.min.css';
src/index.js
Here is the complete code for the index.js file:import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import 'bootstrap/dist/css/bootstrap.min.css'
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById('root')
);
// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import 'bootstrap/dist/css/bootstrap.min.css'
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById('root')
);
// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();
3. EmployeeService - Consume CRUD REST API Call
For our API calls, we will be using Axios. Below is the npm command to install Axios.npm add axios --save
npm add axios --save
EmployeeService.js
Let's create a services folder inside the src folder and create a Javascript file named EmployeeService.js. Inside this file, create EmployeeService class with the following methods to make our HTTP REST call via Axios:import axios from 'axios'
const EMPLOYEE_BASE_REST_API_URL = 'http://localhost:8080/api/v1/employees';
class EmployeeService{
getAllEmployees(){
return axios.get(EMPLOYEE_BASE_REST_API_URL)
}
createEmployee(employee){
return axios.post(EMPLOYEE_BASE_REST_API_URL, employee)
}
getEmployeeById(employeeId){
return axios.get(EMPLOYEE_BASE_REST_API_URL + '/' + employeeId);
}
updateEmployee(employeeId, employee){
return axios.put(EMPLOYEE_BASE_REST_API_URL + '/' +employeeId, employee);
}
deleteEmployee(employeeId){
return axios.delete(EMPLOYEE_BASE_REST_API_URL + '/' + employeeId);
}
}
export default new EmployeeService();
import axios from 'axios'
const EMPLOYEE_BASE_REST_API_URL = 'http://localhost:8080/api/v1/employees';
class EmployeeService{
getAllEmployees(){
return axios.get(EMPLOYEE_BASE_REST_API_URL)
}
createEmployee(employee){
return axios.post(EMPLOYEE_BASE_REST_API_URL, employee)
}
getEmployeeById(employeeId){
return axios.get(EMPLOYEE_BASE_REST_API_URL + '/' + employeeId);
}
updateEmployee(employeeId, employee){
return axios.put(EMPLOYEE_BASE_REST_API_URL + '/' +employeeId, employee);
}
deleteEmployee(employeeId){
return axios.delete(EMPLOYEE_BASE_REST_API_URL + '/' + employeeId);
}
}
export default new EmployeeService();
4. package.json
This file contains all the required dependencies for our React JS project. Most importantly, you can check the current version of React that you are using. It has all the scripts to start, build, and eject our React app:{
"name": "react-hooks-frontend",
"version": "0.1.0",
"private": true,
"dependencies": {
"@testing-library/jest-dom": "^5.14.1",
"@testing-library/react": "^11.2.7",
"@testing-library/user-event": "^12.8.3",
"axios": "^0.21.4",
"bootstrap": "^5.1.1",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-router-dom": "^5.3.0",
"react-scripts": "4.0.3",
"web-vitals": "^1.1.2"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
"eslintConfig": {
"extends": [
"react-app",
"react-app/jest"
]
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
}
}
{
"name": "react-hooks-frontend",
"version": "0.1.0",
"private": true,
"dependencies": {
"@testing-library/jest-dom": "^5.14.1",
"@testing-library/react": "^11.2.7",
"@testing-library/user-event": "^12.8.3",
"axios": "^0.21.4",
"bootstrap": "^5.1.1",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-router-dom": "^5.3.0",
"react-scripts": "4.0.3",
"web-vitals": "^1.1.2"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
"eslintConfig": {
"extends": [
"react-app",
"react-app/jest"
]
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
}
}
5. React List Employee Component
In this section, we will create a new folder called components inside the src folder. Then create a new file called ListEmployeeComponent.js. Within this file create a React class component named ListEmployeeComponent with the following content:import React, {useState, useEffect} from 'react'
import { Link } from 'react-router-dom'
import EmployeeService from '../services/EmployeeService'
const ListEmployeeComponent = () => {
const [employees, setEmployees] = useState([])
useEffect(() => {
getAllEmployees();
}, [])
const getAllEmployees = () => {
EmployeeService.getAllEmployees().then((response) => {
setEmployees(response.data)
console.log(response.data);
}).catch(error =>{
console.log(error);
})
}
const deleteEmployee = (employeeId) => {
EmployeeService.deleteEmployee(employeeId).then((response) =>{
getAllEmployees();
}).catch(error =>{
console.log(error);
})
}
return (
<div className = "container">
<h2 className = "text-center"> List Employees </h2>
<Link to = "/add-employee" className = "btn btn-primary mb-2" > Add Employee </Link>
<table className="table table-bordered table-striped">
<thead>
<th> Employee Id </th>
<th> Employee First Name </th>
<th> Employee Last Name </th>
<th> Employee Email Id </th>
<th> Actions </th>
</thead>
<tbody>
{
employees.map(
employee =>
<tr key = {employee.id}>
<td> {employee.id} </td>
<td> {employee.firstName} </td>
<td>{employee.lastName}</td>
<td>{employee.emailId}</td>
<td>
<Link className="btn btn-info" to={`/edit-employee/${employee.id}`} >Update</Link>
<button className = "btn btn-danger" onClick = {() => deleteEmployee(employee.id)}
style = {{marginLeft:"10px"}}> Delete</button>
</td>
</tr>
)
}
</tbody>
</table>
</div>
)
}
export default ListEmployeeComponent
Let's understand the React Hooks used in the above code snippet.
import React, {useState, useEffect} from 'react'
import { Link } from 'react-router-dom'
import EmployeeService from '../services/EmployeeService'
const ListEmployeeComponent = () => {
const [employees, setEmployees] = useState([])
useEffect(() => {
getAllEmployees();
}, [])
const getAllEmployees = () => {
EmployeeService.getAllEmployees().then((response) => {
setEmployees(response.data)
console.log(response.data);
}).catch(error =>{
console.log(error);
})
}
const deleteEmployee = (employeeId) => {
EmployeeService.deleteEmployee(employeeId).then((response) =>{
getAllEmployees();
}).catch(error =>{
console.log(error);
})
}
return (
<div className = "container">
<h2 className = "text-center"> List Employees </h2>
<Link to = "/add-employee" className = "btn btn-primary mb-2" > Add Employee </Link>
<table className="table table-bordered table-striped">
<thead>
<th> Employee Id </th>
<th> Employee First Name </th>
<th> Employee Last Name </th>
<th> Employee Email Id </th>
<th> Actions </th>
</thead>
<tbody>
{
employees.map(
employee =>
<tr key = {employee.id}>
<td> {employee.id} </td>
<td> {employee.firstName} </td>
<td>{employee.lastName}</td>
<td>{employee.emailId}</td>
<td>
<Link className="btn btn-info" to={`/edit-employee/${employee.id}`} >Update</Link>
<button className = "btn btn-danger" onClick = {() => deleteEmployee(employee.id)}
style = {{marginLeft:"10px"}}> Delete</button>
</td>
</tr>
)
}
</tbody>
</table>
</div>
)
}
export default ListEmployeeComponent
useState() hook
The useState is a Hook (function) that allows you to have state variables in functional components. You pass the initial state to this function and it returns a variable with the current state value (not necessarily the initial state) and another function to update this value. const [employees, setEmployees] = useState([])
const [employees, setEmployees] = useState([])
useEffect() hook
The useEffect Hook allows us to perform side effects (an action) in the function components. It does not use component lifecycle methods that are available in class components. In other words, Effects Hooks are equivalent to componentDidMount(), componentDidUpdate(), and componentWillUnmount() lifecycle methods.We are using the ES6 feature which is a map operator to loop over our employees list and create the view: <tbody>
{
employees.map(
employee =>
<tr key = {employee.id}>
<td> {employee.id} </td>
<td> {employee.firstName} </td>
<td>{employee.lastName}</td>
<td>{employee.emailId}</td>
<td>
<Link className="btn btn-info" to={`/edit-employee/${employee.id}`} >Update</Link>
<button className = "btn btn-danger" onClick = {() => deleteEmployee(employee.id)}
style = {{marginLeft:"10px"}}> Delete</button>
</td>
</tr>
)
}
</tbody>
With the click of the Delete button, the below function will make an API call to delete an Employee record from the database: const deleteEmployee = (employeeId) => {
EmployeeService.deleteEmployee(employeeId).then((response) =>{
getAllEmployees();
}).catch(error =>{
console.log(error);
})
}
With the click of the Update button, we will navigate to the Update Employee page using the following code: <Link className="btn btn-info" to={`/edit-employee/${employee.id}`} >Update</Link>
<tbody>
{
employees.map(
employee =>
<tr key = {employee.id}>
<td> {employee.id} </td>
<td> {employee.firstName} </td>
<td>{employee.lastName}</td>
<td>{employee.emailId}</td>
<td>
<Link className="btn btn-info" to={`/edit-employee/${employee.id}`} >Update</Link>
<button className = "btn btn-danger" onClick = {() => deleteEmployee(employee.id)}
style = {{marginLeft:"10px"}}> Delete</button>
</td>
</tr>
)
}
</tbody>
const deleteEmployee = (employeeId) => {
EmployeeService.deleteEmployee(employeeId).then((response) =>{
getAllEmployees();
}).catch(error =>{
console.log(error);
})
}
<Link className="btn btn-info" to={`/edit-employee/${employee.id}`} >Update</Link>
6. HeaderComponent and FooterComponent
Let's create a new file named HeaderComponent.js and within this file, create a component named HeaderComponent with the following code:import React from 'react'
const HeaderComponent = () => {
return (
<div>
<header>
<nav className = "navbar navbar-expand-md navbar-dark bg-dark">
<div>
<a href = "https://javaguides.net" className = "navbar-brand">
Employee Management Application
</a>
</div>
</nav>
</header>
</div>
)
}
export default HeaderComponent
Let's create a new file named FooterComponent.js and within this file, create a component named FooterComponent with the following code:import React from 'react'
const FooterComponent = () => {
return (
<div>
<footer className = "footer">
<span className="text-muted">All Rights Reserved 2021 @JavaGuides</span>
</footer>
</div>
)
}
export default FooterComponent
import React from 'react'
const HeaderComponent = () => {
return (
<div>
<header>
<nav className = "navbar navbar-expand-md navbar-dark bg-dark">
<div>
<a href = "https://javaguides.net" className = "navbar-brand">
Employee Management Application
</a>
</div>
</nav>
</header>
</div>
)
}
export default HeaderComponent
import React from 'react'
const FooterComponent = () => {
return (
<div>
<footer className = "footer">
<span className="text-muted">All Rights Reserved 2021 @JavaGuides</span>
</footer>
</div>
)
}
export default FooterComponent
7. Configure Routing
To use React Router, you first have to install it using NPM:npm install react-router-dom --save
Let's open the App component and configure routing. We use the Switch element (open and closing tags) these ensure that only one component is rendered at a time.Replace the App component with the following code:import './App.css';
import {BrowserRouter as Router, Route, Switch } from 'react-router-dom';
import FooterComponent from './components/FooterComponent';
import HeaderComponent from './components/HeaderComponent';
import ListEmployeeComponent from './components/ListEmployeeComponent';
import AddEmployeeComponent from './components/AddEmployeeComponent';
function App() {
return (
<div>
<Router>
<HeaderComponent />
<div className= "container">
<Switch>
<Route exact path = "/" component = {ListEmployeeComponent}></Route>
<Route path = "/employees" component = {ListEmployeeComponent}></Route>
<Route path = "/add-employee" component = {AddEmployeeComponent} ></Route>
<Route path = "/edit-employee/:id" component = {AddEmployeeComponent}></Route>
</Switch>
</div>
<FooterComponent />
</Router>
</div>
);
}
export default App;
npm install react-router-dom --save
import './App.css';
import {BrowserRouter as Router, Route, Switch } from 'react-router-dom';
import FooterComponent from './components/FooterComponent';
import HeaderComponent from './components/HeaderComponent';
import ListEmployeeComponent from './components/ListEmployeeComponent';
import AddEmployeeComponent from './components/AddEmployeeComponent';
function App() {
return (
<div>
<Router>
<HeaderComponent />
<div className= "container">
<Switch>
<Route exact path = "/" component = {ListEmployeeComponent}></Route>
<Route path = "/employees" component = {ListEmployeeComponent}></Route>
<Route path = "/add-employee" component = {AddEmployeeComponent} ></Route>
<Route path = "/edit-employee/:id" component = {AddEmployeeComponent}></Route>
</Switch>
</div>
<FooterComponent />
</Router>
</div>
);
}
export default App;
8. Add and Update Employee Component
In this section, we will implement Add Employee and update employee functionality. We will use the same React Component to perform both add and update employee operations.Let's create a new file called AddEmployeeComponent.js. Within this file create a React functional component named AddEmployeeComponent with the following content:import React, {useState, useEffect} from 'react'
import {Link, useHistory, useParams } from 'react-router-dom';
import EmployeeService from '../services/EmployeeService'
const AddEmployeeComponent = () => {
const [firstName, setFirstName] = useState('')
const [lastName, setLastName] = useState('')
const [emailId, setEmailId] = useState('')
const history = useHistory();
const {id} = useParams();
const saveOrUpdateEmployee = (e) => {
e.preventDefault();
const employee = {firstName, lastName, emailId}
if(id){
EmployeeService.updateEmployee(id, employee).then((response) => {
history.push('/employees')
}).catch(error => {
console.log(error)
})
}else{
EmployeeService.createEmployee(employee).then((response) =>{
console.log(response.data)
history.push('/employees');
}).catch(error => {
console.log(error)
})
}
}
useEffect(() => {
EmployeeService.getEmployeeById(id).then((response) =>{
setFirstName(response.data.firstName)
setLastName(response.data.lastName)
setEmailId(response.data.emailId)
}).catch(error => {
console.log(error)
})
}, [])
const title = () => {
if(id){
return <h2 className = "text-center">Update Employee</h2>
}else{
return <h2 className = "text-center">Add Employee</h2>
}
}
return (
<div>
<br /><br />
<div className = "container">
<div className = "row">
<div className = "card col-md-6 offset-md-3 offset-md-3">
{
title()
}
<div className = "card-body">
<form>
<div className = "form-group mb-2">
<label className = "form-label"> First Name :</label>
<input
type = "text"
placeholder = "Enter first name"
name = "firstName"
className = "form-control"
value = {firstName}
onChange = {(e) => setFirstName(e.target.value)}
>
</input>
</div>
<div className = "form-group mb-2">
<label className = "form-label"> Last Name :</label>
<input
type = "text"
placeholder = "Enter last name"
name = "lastName"
className = "form-control"
value = {lastName}
onChange = {(e) => setLastName(e.target.value)}
>
</input>
</div>
<div className = "form-group mb-2">
<label className = "form-label"> Email Id :</label>
<input
type = "email"
placeholder = "Enter email Id"
name = "emailId"
className = "form-control"
value = {emailId}
onChange = {(e) => setEmailId(e.target.value)}
>
</input>
</div>
<button className = "btn btn-success" onClick = {(e) => saveOrUpdateEmployee(e)} >Submit </button>
<Link to="/employees" className="btn btn-danger"> Cancel </Link>
</form>
</div>
</div>
</div>
</div>
</div>
)
}
export default AddEmployeeComponent
Note that we are performing both add and edit operations with a single React functional component.We are using the useState() React hook to store state data: const [firstName, setFirstName] = useState('')
const [lastName, setLastName] = useState('')
const [emailId, setEmailId] = useState('')
We are using the useHistory() hook to get the history object for page navigations. Also, we are using the useParams() hook to get the parameter (id) from the URL. const history = useHistory();
const {id} = useParams();
We are using useEffect() hook: useEffect(() => {
EmployeeService.getEmployeeById(id).then((response) =>{
setFirstName(response.data.firstName)
setLastName(response.data.lastName)
setEmailId(response.data.emailId)
}).catch(error => {
console.log(error)
})
}, [])
import React, {useState, useEffect} from 'react'
import {Link, useHistory, useParams } from 'react-router-dom';
import EmployeeService from '../services/EmployeeService'
const AddEmployeeComponent = () => {
const [firstName, setFirstName] = useState('')
const [lastName, setLastName] = useState('')
const [emailId, setEmailId] = useState('')
const history = useHistory();
const {id} = useParams();
const saveOrUpdateEmployee = (e) => {
e.preventDefault();
const employee = {firstName, lastName, emailId}
if(id){
EmployeeService.updateEmployee(id, employee).then((response) => {
history.push('/employees')
}).catch(error => {
console.log(error)
})
}else{
EmployeeService.createEmployee(employee).then((response) =>{
console.log(response.data)
history.push('/employees');
}).catch(error => {
console.log(error)
})
}
}
useEffect(() => {
EmployeeService.getEmployeeById(id).then((response) =>{
setFirstName(response.data.firstName)
setLastName(response.data.lastName)
setEmailId(response.data.emailId)
}).catch(error => {
console.log(error)
})
}, [])
const title = () => {
if(id){
return <h2 className = "text-center">Update Employee</h2>
}else{
return <h2 className = "text-center">Add Employee</h2>
}
}
return (
<div>
<br /><br />
<div className = "container">
<div className = "row">
<div className = "card col-md-6 offset-md-3 offset-md-3">
{
title()
}
<div className = "card-body">
<form>
<div className = "form-group mb-2">
<label className = "form-label"> First Name :</label>
<input
type = "text"
placeholder = "Enter first name"
name = "firstName"
className = "form-control"
value = {firstName}
onChange = {(e) => setFirstName(e.target.value)}
>
</input>
</div>
<div className = "form-group mb-2">
<label className = "form-label"> Last Name :</label>
<input
type = "text"
placeholder = "Enter last name"
name = "lastName"
className = "form-control"
value = {lastName}
onChange = {(e) => setLastName(e.target.value)}
>
</input>
</div>
<div className = "form-group mb-2">
<label className = "form-label"> Email Id :</label>
<input
type = "email"
placeholder = "Enter email Id"
name = "emailId"
className = "form-control"
value = {emailId}
onChange = {(e) => setEmailId(e.target.value)}
>
</input>
</div>
<button className = "btn btn-success" onClick = {(e) => saveOrUpdateEmployee(e)} >Submit </button>
<Link to="/employees" className="btn btn-danger"> Cancel </Link>
</form>
</div>
</div>
</div>
</div>
</div>
)
}
export default AddEmployeeComponent
const [firstName, setFirstName] = useState('')
const [lastName, setLastName] = useState('')
const [emailId, setEmailId] = useState('')
const history = useHistory();
const {id} = useParams();
useEffect(() => {
EmployeeService.getEmployeeById(id).then((response) =>{
setFirstName(response.data.firstName)
setLastName(response.data.lastName)
setEmailId(response.data.emailId)
}).catch(error => {
console.log(error)
})
}, [])
9. Run React App
Before running React App, make sure that your Spring boot project is up and running.
Use the below command to start the project:npm start
Use yarn to start the project:yarn start
Runs the app in development mode. Open http://localhost:3000 to view it in the browser.
npm start
yarn start
10. Demo of Full-Stack App
The demo of this project is covered in below YouTube video tutorial series:
Code Example on GitHub
This tutorial is accompanied by a working code example on GitHubRecommended tutorial - Learn Spring boot at https://www.javaguides.net/p/spring-boot-tutorial.html
Recommended tutorial - Learn Spring boot at https://www.javaguides.net/p/spring-boot-tutorial.html
Spring Boot React Full Stack Web Development Course on YouTube
Free Spring Boot Tutorial | Full In-depth Course | Learn Spring Boot in 10 Hours
Watch this course on YouTube at Spring Boot Tutorial | Fee 10 Hours Full Course