Web Development

Packaging a React App with Spring Boot

1. Introduction

This is an in-depth article related to building a react app with Spring Boot. Spring Boot framework has features related to creating web based applications. The framework has utilities and annotations to create REST based services.

2. Spring Boot – React App

2.1 Prerequisites

Java 8 or 9 is required on the linux, windows or mac operating system. Maven 3.6.1 is required for building the spring and hibernate application.Want to master Spring Framework ?Subscribe to our newsletter and download the Spring Framework Cookbook right now!In order to help you master the leading and innovative Java framework, we have compiled a kick-ass guide with all its major features and use cases! Besides studying them online you may download the eBook in PDF format!

2.2 Download

You can download Java 8 can be downloaded from the Oracle web site . Apache Maven 3.6.1 can be downloaded from Apache site. Spring framework latest releases are available from the spring website. Node.js is downloaded from the node.js site.

2.3 Setup

You can set the environment variables for JAVA_HOME and PATH. They can be set as shown below:

Environment Setup for Java

JAVA_HOME="/desktop/jdk1.8.0_73"
export JAVA_HOME
PATH=$JAVA_HOME/bin:$PATH
export PATH

The environment variables for maven are set as below:

Environment Setup for Maven

JAVA_HOME=”/jboss/jdk1.8.0_73″
export M2_HOME=/users/bhagvan.kommadi/Desktop/apache-maven-3.6.1
export M2=$M2_HOME/bin
export PATH=$M2:$PATH

2.4 Building the application

2.4.1 Spring

You can start building Spring applications using Spring Boot framework. Spring Boot has a minimal configuration of Spring. Spring Boot has features related to security, tracing, application health management and runtime support for webservers. Spring configuration is done through maven pom.xml. The xml configuration is shown as below:

Spring Configuration

<?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.javacodegeeks.react</groupId>
		<artifactId>react-and-spring-package</artifactId>
		<version>0.0.1-SNAPSHOT</version>
	</parent>
	
	<artifactId>react-and-spring-package-basic</artifactId>
	<version>0.0.1-SNAPSHOT</version>

	<name>React.js and Spring Package</name>
	<description>ReactJS with Spring  Boot REST in the backend</description>

	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<java.version>1.8</java.version>
	</properties>

	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-thymeleaf</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-data-rest</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-devtools</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>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>

			<plugin>
				<groupId>com.github.eirslett</groupId>
				<artifactId>frontend-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>

</project>

You can create a FrameworkController class as the web controller. The class is annotated using @Controller. Controller is used to handle requests in Spring Model View Controller framework. Annotation @RequestMapping is used to annotate the index() method. The code for the FrameworkController class is shown below:

FrameworkController

package org.javacodegeeks.react;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;


@Controller 
public class FrameworkController {

	@RequestMapping(value = "/")
	public String index() {
		return "index"; 
	}

}

ReactAndSpringPackageApplication is created as the Spring Boot web application. When the application starts, beans, ​and settings are wired up dynamically. They are applied to the application context. The code for ReactAndSpringPackageApplication class is shown below:

ReactAndSpringPackageApplication

package org.javacodegeeks.react;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;


@SpringBootApplication
public class ReactAndSpringPackageApplication {

	public static void main(String[] args) {
		SpringApplication.run(ReactAndSpringPackageApplication.class, args);
	}
}

Maven is used for building the application. The command below builds the application.

Maven Command

./mvnw spring-boot:run

The output of the executed command is shown below.

react spring boot - Maven SpringBoot Run
Maven SpringBoot Run

The output on the browser is shown below when the react application is accessed.

react spring boot - React Application
React Application

To create the above application, you need to have the dependencies such as Rest Repositories, Thymeleaf, JPA, and H2 installed.

2.4.1 React Application

You can create the index.html which is shown below:

index.html

<!DOCTYPE html>
<html xmlns:th="https://www.thymeleaf.org">
<head lang="en">
    <meta charset="UTF-8"/>
    <title>React & Spring Package</title>
    <link rel="stylesheet" href="/main.css" />
</head>
<body>

    <div id="react"></div>

    <script src="built/bundle.js"></script>

</body>
</html>

The css file is configured in the above html file. main.css is shown below:

main.css

table {
    border-collapse: collapse;
}

td, th {
    border: 1px solid #999;
    padding: 0.5rem;
    text-align: left;
}

The app.js is created which has a PersonList displayed as a table. app.js is the entry point for the React application.

app.js

'use strict';


const React = require('react'); 
const ReactDOM = require('react-dom'); 
const client = require('./client'); 

class App extends React.Component { 

	constructor(props) {
		super(props);
		this.state = {persons: []};
	}

	componentDidMount() { 
		client({method: 'GET', path: '/api/persons'}).done(response => {
			this.setState({persons: response.entity._embedded.persons});
		});
	}

	render() { 
		return (
			<PersonList persons={this.state.persons}/>
		)
	}
}


class PersonList extends React.Component{
	render() {
		const persons = this.props.persons.map(person =>
			<Person key={person._links.self.href} person={person}/>
		);
		return (
			<table>
				<tbody>
					<tr>
						<th>First Name</th>
						<th>Last Name</th>
						<th>Comments</th>
					</tr>
					{persons}
				</tbody>
			</table>
		)
	}
}
class Person extends React.Component{
	render() {
		return (
			<tr>
				<td>{this.props.person.firstName}</td>
				<td>{this.props.person.lastName}</td>
				<td>{this.props.person.comments}</td>
			</tr>
		)
	}
}
ReactDOM.render(
	<App />,
	document.getElementById('react')
)

npm is used to install the packages specified in package.json which is shown below

package.json

{
  "name": "spring-data-rest-and-reactjs",
  "version": "0.1.0",
  "description": "Packaging React with Spring",
  "repository": {
    "type": "git",
    "url": ""
  },
  "keywords": [
    "rest",
    "hateoas",
    "spring",
    "data",
    "react"
  ],
  "author": "Bhagvan Kommadi",
  "license": "Apache-2.0",
  "bugs": {
    "url": ""
  },
  "homepage": "",
  "dependencies": {
    "react": "^16.5.2",
    "react-dom": "^16.5.2",
    "rest": "^1.3.1"
  },
  "scripts": {
    "watch": "webpack --watch -d --output ./target/classes/static/built/bundle.js"
  },
  "devDependencies": {
    "@babel/core": "^7.1.0",
    "@babel/preset-env": "^7.1.0",
    "@babel/preset-react": "^7.0.0",
    "babel-loader": "^8.0.2",
    "webpack": "^4.19.1",
    "webpack-cli": "^3.1.0"
  }
}

webpack.config.js is used to configure app.js. The file is shown below

package.json

var path = require('path');

module.exports = {
    entry: './src/main/js/app.js',
    devtool: 'sourcemaps',
    cache: true,
    mode: 'development',
    output: {
        path: __dirname,
        filename: './src/main/resources/static/built/bundle.js'
    },
    module: {
        rules: [
            {
                test: path.join(__dirname, '.'),
                exclude: /(node_modules)/,
                use: [{
                    loader: 'babel-loader',
                    options: {
                        presets: ["@babel/preset-env", "@babel/preset-react"]
                    }
                }]
            }
        ]
    }
};

2.4.2 JPA Classes

H2DatabaseLoader class is created to load the H2 database. It implements CommandLineRunner interface and it’s run method is executed after the creation of beans and registry. The code of the H2DatabaseLoader class is shown below:

H2DatabaseLoader

package org.javacodegeeks.react;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;


@Component 
public class H2DatabaseLoader implements CommandLineRunner { 

	private final PersonRepository repository;

	@Autowired 
	public H2DatabaseLoader(PersonRepository repository) {
		this.repository = repository;
	}

	@Override
	public void run(String... strings) throws Exception { 
		this.repository.save(new Person("John", "Smith", "Doctor"));
        this.repository.save(new Person("Brad", "Smith", "Engineer"));
        this.repository.save(new Person("Gregory", "Smith", "Mechanic"));
	}
}


Person class has attributes id, firstName, lastName, and comments. The class has a constructor with arguments for firstName, lastName, and comments properties. id is a generated property. The class source code is shown below.

Person Class

package org.javacodegeeks.react;

import java.util.Objects;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;


@Entity 
public class Person {

	private @Id @GeneratedValue Long id;
	private String firstName;
	private String lastName;
	private String comments;

	private Person() {}

	public Person(String firstName, String lastName, String comments) {
		this.firstName = firstName;
		this.lastName = lastName;
		this.comments = comments;
	}

	@Override
	public boolean equals(Object object) {
		if (this == object) return true;
		if (object == null || getClass() != object.getClass()) return false;
		Person person = (Person) object;
		return Objects.equals(id, person.id) &&
			Objects.equals(firstName, person.firstName) &&
			Objects.equals(lastName, person.lastName) &&
			Objects.equals(comments, person.comments);
	}

	@Override
	public int hashCode() {

		return Objects.hash(id, firstName, lastName, comments);
	}

	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 getComments() {
		return comments;
	}

	public void setComments(String comments) {
		this.comments = comments;
	}

	@Override
	public String toString() {
		return "Person{" +
			"id=" + id +
			", firstName='" + firstName + '\'' +
			", lastName='" + lastName + '\'' +
			", comments='" + comments + '\'' +
			'}';
	}
}


PersonRepository which extends a generic Class CrudRepository<Person,Long> is shown below:

Person Repository

package org.javacodegeeks.react;

import org.springframework.data.repository.CrudRepository;


public interface PersonRepository extends CrudRepository<Person, Long> { 

}


The REST api root URI is specified in application .properties which is shown below.

application.properties

spring.data.rest.base-path=/api

Hypermedia as the Engine of Application State (HATEOAS) helps in modifying the URI without changing the clients.

2.5 Best Practices for unit testing

You can isolate the functionality to be unit tested. This is done by limiting the context of loaded frameworks and components. The slices of functionality are loaded when testing spring boot applications. The other best practices are available at the spring boot testing site.

2.6 Error Handling

Spring boot framework has features to handle exceptions and errors. Errors in REST APIs help in presenting the issues to the clients. You can use @ResponseStatus annotation to specify the Response Status for a specific exception.

2.7 Logging

Spring Boot Framework uses Commons Logging for application logging. Different configurations for logging are provided in the framework. They are Java Util Logging, Log4J2, and Logback. Loggers are pre-configured for printing the output on the console or in the configured file.

3. Download the Source Code

Download
You can download the full source code of this example here: Packaging a React App with Spring Boot

Bhagvan Kommadi

Bhagvan Kommadi is the Founder of Architect Corner & has around 19 years experience in the industry, ranging from large scale enterprise development to helping incubate software product start-ups. He has done Masters in Industrial Systems Engineering at Georgia Institute of Technology (1997) and Bachelors in Aerospace Engineering from Indian Institute of Technology, Madras (1993). He is member of IFX forum,Oracle JCP and participant in Java Community Process. He founded Quantica Computacao, the first quantum computing startup in India. Markets and Markets have positioned Quantica Computacao in ‘Emerging Companies’ section of Quantum Computing quadrants. Bhagvan has engineered and developed simulators and tools in the area of quantum technology using IBM Q, Microsoft Q# and Google QScript.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Inline Feedbacks
View all comments
Back to top button