Disclosure: This article may contain affiliate links. When you purchase, we may earn a small commission.

Fullstack Spring Boot + React.js Tutorial Example for Java Developers

Hello Java programmers, if you want to learn Spring Boot and Reactjs and looking for a full-stack Java example then you have come to the right place. Earlier, I have shared the best Spring Boot coursesbooks, and best reactjs courses as well as many courses to learn full-stack development in Java and become a full-stack java developer. In this tutorial, we will look at creating an application that is capable of creating, deleting, updating, and retrieving functions using the Spring Boot, React, and H2 Database. Here we create a School Classroom Application that can insert, update, delete and search students. The frontend of the application is developed using reactjs and also the backend is developed using spring boot. Here, the communication between the two platforms will be done using the REST APIs. 


Tools and technologies which is used in this application.

  • You can use any IDE to develop the backend(spring boot) and the frontend of the application.
  • Server: Apache Tomcat
  • Spring Boot 2
  • Reactjs
  • H2 Database

So let's create the spring boot backend of the system first. 

Spring Boot + React.js Example for Java Developers



The Spring Boot Application

Here, the REST API is used to communicate with the frontend(angular) of the application. Before you start programming, you need to have a better structure of the project. So below is the project structure which is used in this application.



This application is used to store some data in the in-memory database of H2 and fetch those data. So below are the maven dependencies in the pom.xml file which is used in this example.


<?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.6.0</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.student</groupId>
<artifactId>crudapp</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>crudapp</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-jdbc</artifactId>
</dependency>

<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>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</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>


The Application.properties file

SpringApplication will load properties from application.properties files in the following locations and add them to the Spring Environment: 
        1. config subdirectory of the current directory. 
        2. The current directory 
        3. A classpath /config package 
        4. The classpath root


Below is the used applicaiton.properties file in this Student Crud application.

server.port=8090
spring.h2.console.enabled=true
spring.h2.console.path=/h2-console
spring.datasource.url=jdbc:h2:mem:cmpe172
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
 

JPA Entity Class

Below is the used JPA entity class in this application. This is responsible for modeling Students.

package com.student.crudapp.model;

import javax.persistence.*;

@Entity
@Table(name = "STUDENT")
public class Student {

@Column(name = "id")
@Id
@GeneratedValue(strategy=GenerationType.AUTO)
private int id;

@Column(name = "name")
private String name;

@Column(name = "email")
private String email;

@Column(name = "grade")
private String grade;

public int getId() {
return id;
}

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

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public String getEmail() {
return email;
}

public void setEmail(String email) {
this.email = email;
}

public String getGrade() {
return grade;
}

public void setGrade(String grade) {
this.grade = grade;
}

@Override
public String toString() {
return "Student{" +
"id=" + id +
", name='" + name + '\'' +
", email='" + email + '\'' +
", grade='" + grade + '\'' +
'}';
}
}

The StudentRepository Interface

As we need to stick with the crud functionality of our system, we need to configure our Spring Data repository, StudentRepository interface as a Crud repository as below.

package com.student.crudapp.repository;

import com.student.crudapp.model.Student;
import org.springframework.data.jpa.repository.JpaRepository;

import java.util.List;

public interface StudentRepository extends JpaRepository<Student, Integer> {

List<Student> findAll();
Student findById(int id);

}

The StudentController Class


Below is the StudentController class which is used in the application. There, we implement the addStudent, findStudent, getAllStudents, updateStudent and deleteStudent methods which are communicating with the H2 database in order to store them in the in-memory database.


package com.student.crudapp.controller;

import com.student.crudapp.model.Student;
import com.student.crudapp.repository.StudentRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@Controller
@CrossOrigin(origins = "http://localhost:8090")
public class StudentController {

@Autowired
StudentRepository studentRepository;

//check the api's working correctly api
@RequestMapping(value="/ping", method=RequestMethod.GET)
@ResponseBody
public String healthCheck() {
return "This is working well";
}


@RequestMapping(value="/students", method=RequestMethod.GET)
@ResponseBody
public List<Student> getAllStudents() {
return studentRepository.findAll();
}

@RequestMapping(value="/student", method=RequestMethod.POST)
@ResponseBody
public Student addStudent(Student student) {
return studentRepository.save(student);
}

@RequestMapping(value="/findstudent", method = RequestMethod.GET)
@ResponseBody
public Student findStudent(@RequestParam("studentId") int studentId) {
return studentRepository.findById(studentId);
}

@RequestMapping(value= "/updatestudent", method = RequestMethod.GET)
@ResponseBody
public Student updateStudent(@RequestBody Student student){
return studentRepository.save(student);
}

@RequestMapping(value="/deletestudent", method = RequestMethod.GET)
@ResponseBody
public int deleteStudent(@RequestParam("studentId") int studentId) {
return studentRepository.deleteById(studentId);
}
}

In the above controller, we used the @CrossOrigin annotation, in order to enable Cross-Origin Resource Sharing (CORS) on the server.
You think this is unncessary, but the thing is we're deploying our Angular frontend to http://localhost:4200, and our Boot backend to http://localhost:8090, the browser would otherwise deny requests from one to the other. the server.

So below are the created API's in order to deal with frontend of the application.
1. Add a new Student (POST request)
http://localhost:8090/student
{
    "name""Test",
    "email""test@gmail.com",
    "grade""05"
}

2. Get all students (GET request)
http://localhost:8090/students
3. Find specific student(GET request)
http://localhost:8090/findstudent?studentId=1
4. Update student(GET Request)
http://localhost:8090/updatestudent
{
    "id"1,
    "name""Testupdated",
    "email""testupdated@gmail.com",
    "grade""05"
}

5. Delete student(GET request)
http://localhost:8090/deletestudent?studentId=1

Here is the screenshot of the H2 database that we have created.


So now the backend of the application is completed, then let's focus on the frontend of the application, which we use reactjs to develop our application.

The Reactjs Application.

Before going into frontend development, we need to look at what is reactjs. Who develops this and what advantages that you can get by using this to develop your applications.

React is a front-end JavaScript library for creating user interfaces using UI components that is free and open-source. It is maintained by Meta and a community of individual developers and companies. React can be used as a base in the development of single-page or mobile applications.

Advantages of using Reactjs
        1. Easy to learn and use - React is easy to learn and easy to use and comes with a good supply of documentation, tutorials, and training resources.
        2. Reusable Component - Components are wonderful and React is based on them. You start with small things, which you use to build bigger things, which you use to build apps.
        3. The virtual DOM - React solves this by using a virtual DOM. This is, as the name implies, a virtual representation of the DOM. Any new view changes are first performed on the virtual DOM, which lives in memory and not on your screen.
        4. Easy to write with JSX - JSX produces React “elements” and has a number of side benefits, including helping prevent injection attacks.

So let's create our application. Before that, you need to make sure that, In your computer, you have the nodejs.

Creating React App is a comfortable environment for learning React, and is the best way to start building a new single-page application in React.

       $  npx create-react-app react-frontend

This command will initiate the project with all the node modules.

        $ cd react-frontend

This will navigate to your project folder.

       $ npm start

This will start the project and it will run on default port 3000. You can access the created project using the http://localhost:3000/

Reactjs Project Structure.


The project structure of our Student Management Application is given above.

React Application Entry Point

After the app creation process completes, navigate into the app directory and install Bootstrap, cookie support for React, React Router, and Reactstrap. Modify app/src/App.js to use the following code that calls to routing to other pages.

import React from 'react';
import logo from './logo.svg';
import './App.css';
import {BrowserRouter as Router, Route, Switch} from 'react-router-dom'
import ListStudentComponent from './components/ListStudentComponent';
import HeaderComponent from './components/HeaderComponent';
import FooterComponent from './components/FooterComponent';
import CreateStudentComponent from './components/CreateStudentComponent';
import UpdateStudentComponent from './components/UpdateStudentComponent';
import ViewStudentComponent from './components/ViewStudentComponent';

function App() {
  return (
    <div>
        <Router>
              <HeaderComponent />
                <div className="container">
                    <Switch>
                          <Route path = "/"
                            exact component = {ListStudentComponent}></Route>
                          <Route path = "/students"
                            component = {ListStudentComponent}></Route>
                          <Route path = "/add-student/:id"
                            component = {CreateStudentComponent}></Route>
                          <Route path = "/view-student/:id"
                            component = {ViewStudentComponent}></Route>
                          <Route path = "/update-student/:id"
                            component = {UpdateStudentComponent}></Route>
                    </Switch>
                </div>
              <FooterComponent />
        </Router>
    </div>
   
  );
}

export default App;



Build Components

React is all about components, and you don’t want to render everything in your main App, so create the following components and populate them with javascript.

  • CreateStudentComponent.jsx
  • FooterComponent.jsx
  • HeaderComponent.jsx
  • ListStudentComponent.jsx
  • UpdateStudentComponent.jsx
  • ViewStudentComponent.jsx  

CreateStudentComponent.jsx

The CreateStudentComponent.jsx file looks like below.

import React, { Component } from 'react'
import StudentService from '../services/StudentService';

class CreateStudentComponent extends Component {
    constructor(props) {
        super(props)

        this.state = {
            // step 2
            id: this.props.match.params.id,
            name: '',
            email: '',
            grade: ''
        }
        this.changeNameHandler = this.changeNameHandler.bind(this);
        this.changeEmailHandler = this.changeEmailHandler.bind(this);
        this.changeGradeHandler = this.changeGradeHandler.bind(this);

        this.saveOrUpdateStudent = this.saveOrUpdateStudent.bind(this);
    }

    // step 3
    componentDidMount(){

        // step 4
        if(this.state.id === '_add'){
            return
        }else{
            StudentService.getStudentById(this.state.id).then( (res) =>{
                let student = res.data;
                this.setState({
                    name: student.name,
                    email: student.email,
                    grade : student.grade
                });
            });
        }        
    }
    saveOrUpdateStudent = (e) => {
        e.preventDefault();
        let student = {name: this.state.name, email: this.state.email, grade: this.state.grade};
        console.log('Student => ' + JSON.stringify(student));

        // step 5
        if(this.state.id === '_add'){
            StudentService.createStudent(student).then(res =>{
                this.props.history.push('/students');
            });
        }else{
            StudentService.updateStudent(student, this.state.id).then( res => {
                this.props.history.push('/students');
            });
        }
    }
   
    changeNameHandler= (event) => {
        this.setState({name: event.target.value});
    }

    changeGradeHandler= (event) => {
        this.setState({grade: event.target.value});
    }

    changeEmailHandler= (event) => {
        this.setState({email: event.target.value});
    }

    cancel(){
        this.props.history.push('/students');
    }

    getTitle(){
        if(this.state.id === '_add'){
            return <h3 className="text-center">Add Student</h3>
        }else{
            return <h3 className="text-center">Update Student</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> Name: </label>
                                            <input placeholder="Name" name="Name" className="form-control"
                                                value={this.state.name} onChange={this.changeNameHandler}/>
                                        </div>
                                        <div className = "form-group">
                                            <label> Email: </label>
                                            <input placeholder="Email Address" name="email" className="form-control"
                                                value={this.state.email} onChange={this.changeEmailHandler}/>
                                        </div>
                                        <div className = "form-group">
                                            <label> Grade: </label>
                                            <input placeholder="Grade" name="grade" className="form-control"
                                                value={this.state.grade} onChange={this.changeGradeHandler}/>
                                        </div>
 

                                        <button className="btn btn-success" onClick={this.saveOrUpdateStudent}>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 CreateStudentComponent


This file is used to create new students by sending the user data to the backend of the system.


ListStudentComponent.jsx

In reactjs, all the things are made out of components. Here, also we have a ListStudentComponent.jsx to display the students.

import React, { Component } from 'react'
import StudentService from '../services/StudentService'

class ListStudentComponent extends Component {
    constructor(props) {
        super(props)

        this.state = {
                students: []
        }
        this.addStudent = this.addStudent.bind(this);
        this.editStudent = this.editStudent.bind(this);
        this.deleteStudent = this.deleteStudent.bind(this);
    }

    deleteStudent(id){
        StudentService.deleteStudent(id).then( res => {
            this.setState({students: this.state .students .filter(student => student.id !== id)});
        });
    }
    viewStudent(id){
        this.props.history.push(`/view-student/${id}`);
    }
    editStudent(id){
        this.props.history.push(`/add-student/${id}`);
    }

    componentDidMount(){
        StudentService.getStudent().then((res) => {
            this.setState({ students: res.data});
        });
    }

    addStudent(){
        this.props.history.push('/add-student/_add');
    }

    render() {
        return (
            <div>
                 <h2 className="text-center">Students List</h2>
                 <div className = "row">
                    <button className="btn btn-primary" onClick={this.addStudent}> Add Student</button>
                 </div>
                 <br></br>
                 <div className = "row">
                        <table className = "table table-striped table-bordered">

                            <thead>
                                <tr>
                                    <th> Student Name</th>
                                    <th> Student Email</th>
                                    <th> Student Grade</th>
                                    <th> Actions</th>
                                </tr>
                            </thead>
                            <tbody>
                                {
                                    this.state.students.map(
                                        student =>
                                        <tr key = {student.id}>
                                             <td> { student.firstName} </td>  
                                             <td> {student.lastName}</td>
                                             <td> {student.emailId}</td>
                                             <td>
                                                 <button onClick={ () => this.editStudent(student.id)} className="btn btn-info">Update </button>
                                                 <button style={{marginLeft: "10px"}} onClick={ () => this.deleteStudent(student.id)} className="btn btn-danger">Delete </button>
                                                 <button style={{marginLeft: "10px"}} onClick={ () => this.viewStudent(student.id)} className="btn btn-info">View </button>
                                             </td>
                                        </tr>
                                    )
                                }
                            </tbody>
                        </table>

                 </div>

            </div>
        )
    }
}

export default ListStudentComponent



UpdateStudentComponent.jsx

This is the file that we used to update a record in the student management application. The records will be sent to the API and will update.  

import React, { Component } from 'react'
import StudentService from '../services/StudentService';

class UpdateStudentComponent extends Component {
    constructor(props) {
        super(props)

        this.state = {
            id: this.props.match.params.id,
            name: '',
            email: '',
            grade: ''
        }
        this.changeNameHandler = this.changeFirstNameHandler.bind(this);
        this.changeEmailHandler = this.changeEmailHandler.bind(this);
        this.changeGradeHandler = this.changeGradeHandler.bind(this);
        this.updateStudent = this.updateStudent.bind(this);
    }

    componentDidMount(){
        StudentService.getStudentById(this.state.id).then( (res) =>{
            let student = res.data;
            this.setState({name: student.name,
                email: student.email,
                grade : student.grade
            });
        });
    }

    updateStudent = (e) => {
        e.preventDefault();
        let student = {name: this.state.name, email: this.state.email, grade: this.state.grade};
        console.log('student => ' + JSON.stringify(student));
        console.log('id => ' + JSON.stringify(this.state.id));
        StudentService.updateStudent(student, this.state.id).then( res => {
            this.props.history.push('/students');
        });
    }
   
    changeNameHandler= (event) => {
        this.setState({name: event.target.value});
    }

    changeGradeHandler= (event) => {
        this.setState({grade: event.target.value});
    }

    changeEmailHandler= (event) => {
        this.setState({emailId: event.target.value});
    }

    cancel(){
        this.props.history.push('/students');
    }

    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 Employee</h3>
                                <div className = "card-body">
                                    <form>
                                        <div className = "form-group">
                                            <label> Name: </label>
                                            <input placeholder="First Name" name="firstName" className="form-control"
                                                value={this.state.firstName} onChange={this.changeNameHandler}/>
                                        </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>
                                        <div className = "form-group">
                                            <label> Grade: </label>
                                            <input placeholder="Last Name" name="lastName" className="form-control"
                                                value={this.state.lastName} onChange={this.changeLastNameHandler}/>
                                        </div>
                                        <button className="btn btn-success" onClick={this.updateStudent}>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 UpdateStudentComponent


This will help to update a student record.


ViewStudentComponent.jsx

This will help to view one student out of many students.

import React, { Component } from 'react'
import StudentService from '../services/StudentService'

class ViewStudentComponent extends Component {
    constructor(props) {
        super(props)

        this.state = {
            id: this.props.match.params.id,
            students: {}
        }
    }

    componentDidMount(){
        StudentService.getStudentById(this.state.id).then( res => {
            this.setState({students: res.data});
        })
    }

    render() {
        return (
            <div>
                <br></br>
                <div className = "card col-md-6 offset-md-3">
                    <h3 className = "text-center"> View Student Details</h3>
                    <div className = "card-body">
                        <div className = "row">
                            <label> Student Name: </label>
                            <div> { this.state.students.name }</div>
                        </div>
                        <div className = "row">
                            <label> Student Email : </label>
                            <div> { this.state.students.email }</div>
                        </div>
                        <div className = "row">
                            <label> Student Grade: </label>
                            <div> { this.state.students.grade }</div>
                        </div>
                    </div>

                </div>
            </div>
        )
    }
}

export default ViewStudentComponent





FooterComponent.jsx and HeaderComponent.jsx

These files are used to customize the screen of the Student Management Application. These files are displayed below.

FooterComponent.jsx

import React, { Component } from 'react'

class FooterComponent extends Component {
    constructor(props) {
        super(props)

        this.state = {
                 
        }
    }

    render() {
        return (
            <div>
                <footer className = "footer">
                    <span className="text-muted">All Rights Reserved</span>
                </footer>
            </div>
        )
    }
}

export default FooterComponent



HeaderComponent.jsx

import React, { Component } from 'react'

class HeaderComponent extends Component {
    constructor(props) {
        super(props)

        this.state = {
                 
        }
    }

    render() {
        return (
            <div>
                <header>
                    <nav className="navbar navbar-expand-md navbar-dark bg-dark">
                    <div><a href="" className="navbar-brand">Student Management Application</a></div>
                    </nav>
                </header>
            </div>
        )
    }
}

export default HeaderComponent



API Integration

This is a file that is used to integrate our test our application.  This file has all the API calls which are used to communicate with our backend of the system.(Spring boot)

import axios from 'axios';

const STUDENT_API_BASE_URL = "http://localhost:8090";

class StudentService {

    getStudent(){
        return axios.get(STUDENT_API_BASE_URL);
    }

    createStudent(student){
        return axios.post(STUDENT_API_BASE_URL+'/student', student);
    }

    getStudentById(studentId){
        return axios.get(STUDENT_API_BASE_URL + '/findstudent' + studentId);
    }

    updateStudent(student, studentId){
        return axios.put(STUDENT_API_BASE_URL + '/updatestudent' + studentId, student);
    }

    deleteStudent(studentId){
        return axios.delete(STUDENT_API_BASE_URL + '/deletestudent' + studentId);
    }
}

export default new StudentService()


Interfaces of the project.

The interfaces of the project are given below.






Summary

Now we know how to use Spring Boot React CRUD to create a CRUD app that communicates with an H2 database. We also look at client-server architecture for REST APIs using Spring Web MVC and Spring Data JPA, as well as the structure of a React project for creating a front-end app that makes HTTP queries and consumes replies. So hope to see you in the next tutorial with another interesting topic.

Other Java and Spring Tutorial you may like
  • How to update an entity using Spring Data JPA? (example)
  • 20 Spring Boot Interview Questions with answers (questions)
  • What is @Conditional annotation in Spring? (conditional example)
  • How Spring MVC works internally? (answer)
  • Spring Data JPA @Query Example (query example)
  • 10 Advanced Spring Boot Courses for Java developers (courses)
  • Spring Data JPA Repository Example in Java (JpaReposistory example)
  • 20+ Spring MVC Interview Questions for Programmers (answer)
  • 13 Spring Boot Actuator Interview questions (boot questions)
  • Difference between @Autowired and @Inject in Spring? (answer)
  • Top 5 Frameworks Java Developer Should Know (frameworks)
  • Difference between @RequestParam and @PathVariable in Spring (answer)
  • Top 7  Courses to learn Microservices in Java (courses)
  • How to use @Bean in Spring framework (Example)
  • How to fix No property type found in Spring Data JPA? [Solution]
  • 5 Spring Cloud annotations Java programmer should learn (cloud)
  • Top 5 Courses to Learn and Master Spring Cloud (courses)
  • 5 Courses to Learn Spring Security for Java programmers (courses)
  • 10 Spring MVC annotations Java developer should know (annotations)
  • @SpringBootApplication vs. @EnableAutoConfiguration? (answer)
  • 15 Spring Boot Interview Questions for Java Developers (questions)
  • Difference between @Component, @Service, and @Controller in Spring (answer)

Thanks for reading this article so far. If you find this full-stack Spring Boot and Reactjs tutorial and example, then please share it with your friends and colleagues. If you have any questions or feedback, then please drop a note.

P. S. - If you are a Spring Boot beginner and want to learn the Spring Boot framework from scratch and look for some of the best online resources, you can also check out these best Spring Boot courses for Java developersThis list contains free Udemy and Pluralsight courses to learn Spring Boot from scratch.  

1 comment :

w580ix said...

The source code is in a very terrible format. StudentService Where do you mention/create this service in the tutorial? There are a lot of topics that aren't explained.

Post a Comment