How to integrate a React webapp inside a spring boot Application with jar packaging

We have a react webapp and a spring boot application(built with maven) in development.
The React app(on npm) and boot app are running separately but time has come to now integrate them and put it into QA/staging.
We are using webpack for bundling the react application.
The boot app is a single module with REST API(consumed by the react front-end) with hibernate for database persistence.

Questions

  1. What should be the directory structure for both react and boot app? We want to deploy the whole app(front-end and back-end) as a .jar (spring boot uberjar)
  2. What is the react app development workflow ? A workflow without having to run maven and run java -jar every time a small css, html or .js change is made.

I found resources on web where both the react and the boot applications were on separate repos/directories without any integration, which is not optimal; I am yet to find a resource where both webapp resources and boot resources are in the same directory tree which also accounts for development lifecycle and production lifecycle.

Bonus : The react developers know only html/js/css. Is it possible that they have only the react app resources in their vscode/webstorm project ?

Answers:

Thank you for visiting the Q&A section on Magenaut. Please note that all the answers may not help you solve the issue immediately. So please treat them as advisements. If you found the post helpful (or not), leave a comment & I’ll get back to you as soon as possible.

Method 1

You can run React and SpringBoot on the same port and Package them as a single artifact !!
Please follow the steps that I have explained here, that should get you up and running.
To answer your questions-

  1. The directory structure has been show below. Both will be under the same root directory.
  2. Regarding react app development workflow- you can just develop it the way you develop a front-end react project, with hot reloads(save the file and the changes will reflect automatically) and meaningful error messages. You will have to use proxying to communicate.

Here is the Github link of the demo project that I am going to
explain here

Spring Boot can serve static content from src/main/resources/static folder. We will exploit the above mentioned feature of Spring Boot to serve the single page of the react project. We will serve a html page from the static folder in the target directory, not in the source directory.

The Project structure

How to integrate a React webapp inside a spring boot Application with jar packaging

First, create a spring boot project with https://start.spring.io. Add the Web dependency. Set the groupId and artifactId to whatever you want. Generate the project and unzip it into your project directory.

Or, if you are using Spring Tools Suite you can simply click
File->New->Spring Starter Project and mention the required details to create a spring boot project.

The frontend folder inside src/main should have your react application build using create-react-app.

So, there are two steps-

  1. create a production build of the frontend.
  2. copy the production build into ${target/classes/}.

We we will use two maven plugins and Thymleaf for that.

  1. frontend-maven-plugin for step 1.
  2. maven-resources-plugin for step 2.

For frontend-maven-plugin at Step 1– If you closely look at the pom.xml there I have mentioned the src directroy from where frontend-maven-plugin will take the files, create the production build and place the contents inside the output directory mentioned(inside src/main/frontend/build).

 <workingDirectory>${frontend-src-dir}</workingDirectory>
 <installDirectory>${project.build.directory}</installDirectory>

For maven-resources-plugin at step 2– It will take the production build that was just created by frontend-maven-plugin and place it inside your root directory then target/classes/static.

Then we will use Thymleaf to serve the static content from the target/classes/static using a rest endpoint in the controller. Or else you have to type in the name of the html file, like http://localhost:8080/index.html

Your pom.xml should look like this-

<?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.4.2</version>
        <relativePath /> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.springreact</groupId>
    <artifactId>demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>demo</name>
    <description>Run React Frontend and SpringBoot Backend on the same port.</description>
    <properties>
        <java.version>1.8</java.version>
        <frontend-src-dir>${project.basedir}/src/main/frontend</frontend-src-dir>
        <node.version>v14.15.4</node.version>
        <yarn.version>v1.16.0</yarn.version>
        <frontend-maven-plugin.version>1.7.6</frontend-maven-plugin.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </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>
        <dependency>
            <groupId>org.springframework.boot</groupId> 
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </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>
            <plugin>
                <groupId>com.github.eirslett</groupId>
                <artifactId>frontend-maven-plugin</artifactId>
                <version>${frontend-maven-plugin.version}</version>

                <configuration>
                    <nodeVersion>${node.version}</nodeVersion>
                    <yarnVersion>${yarn.version}</yarnVersion>
                    <workingDirectory>${frontend-src-dir}</workingDirectory>
                    <installDirectory>${project.build.directory}</installDirectory>
                </configuration>

                <executions>
                    <execution>
                        <id>install-frontend-tools</id>
                        <goals>
                            <goal>install-node-and-yarn</goal>
                        </goals>
                    </execution>

                    <execution>
                        <id>yarn-install</id>
                        <goals>
                            <goal>yarn</goal>
                        </goals>
                        <configuration>
                            <arguments>install</arguments>
                        </configuration>
                    </execution>

                    <execution>
                        <id>build-frontend</id>
                        <goals>
                            <goal>yarn</goal>
                        </goals>
                        <phase>prepare-package</phase>
                        <configuration>
                            <arguments>build</arguments>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <artifactId>maven-resources-plugin</artifactId>
                <executions>
                    <execution>
                        <id>position-react-build</id>
                        <goals>
                            <goal>copy-resources</goal>
                        </goals>
                        <phase>prepare-package</phase>
                        <configuration>
                            <outputDirectory>${project.build.outputDirectory}/static</outputDirectory>
                            <resources>
                                <resource>
                                    <directory>${frontend-src-dir}/build</directory>
                                    <filtering>false</filtering>
                                </resource>
                            </resources>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>

Here is the Controller code.
package com.springreact.demo.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.servlet.ModelAndView;

@Controller
public class IndexController {

    @GetMapping("")
    public ModelAndView home() {
        ModelAndView mav=new ModelAndView("index");
        return mav;
    }

}

If you follow the above mentioned steps , you should see your React App being spun up on http://localhost:8080/.

How to integrate a React webapp inside a spring boot Application with jar packaging

For further details you can checkout the blog that I have written on it. Here are the links of the blog on two different platforms-

Dev Community- https://dev.to/arpan_banerjee7/run-react-frontend-and-springboot-backend-on-the-same-port-and-package-them-as-a-single-artifact-14pa

Medium- https://medium.com/codex/run-react-frontend-and-springboot-backend-on-the-same-port-and-package-them-as-a-single-artifact-a790c9e10ac1

Method 2

I would suggest creating multimodule maven project, where one module will be spring-boot app and second one react app. To make it into uber jar, pack react app into webjar build by maven.
Building can be done also by maven with proper plugin.

So:

  1. Create normal react app (build by npm and so on) in sub directory of you project
  2. Add to this app pom.xml which will compile javascript on maven compile and pack it into proper webjar structure
  3. In spring-boot module add react one as normal maven dependency. Check out this tutorial

This will allow you to have everything as one uber jar app. On the other hand, front-end people can work with react module as with any other react application.

Method 3

I found this post that may answer your question. It shows how to develop the react front end and spring boot back end and then merge them into a single jar file for deployment.

https://medium.com/@mukundmadhav/build-and-deploy-react-app-with-spring-boot-and-mysql-6f888eb0c600

Method 4

To answer the question on whether it is best practice to create an uber jar or to package the app in a single jar. It depends on certain things. Like for ex. If you have oauth2 implemented in your app and would like to access the rest end points without having to implement the logic of access token and such. You could put it in a single jar. else you’d have to create a header with the access token in the header.


All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

0 0 votes
Article Rating
Subscribe
Notify of
guest

0 Comments
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
()
x