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.
The output on the browser is shown below when the react application is accessed.
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
You can download the full source code of this example here: Packaging a React App with Spring Boot