paint-brush
Package Your React App with Spring Boot [A How-To Guide]by@natural_e
15,722 reads
15,722 reads

Package Your React App with Spring Boot [A How-To Guide]

by EmmanuellaFebruary 24th, 2020
Read on Terminal Reader
Read this story w/o Javascript
tldt arrow

Too Long; Didn't Read

Package Your React App with Spring Boot [A How-To Guide] Read Me, gitignore, license, etc. and create an empty repo on Github. Create React App gives you all the basic things you need to get up and running asap. Spring Boot helps you start and maintain a spring application quickly and easily. Frontend and backend in a single war file with optimized productions build. For example, the DadJokes Controller is created in the same folder as SpringReactAppApplication file.

Companies Mentioned

Mention Thumbnail
Mention Thumbnail
featured image - Package Your React App with Spring Boot [A How-To Guide]
Emmanuella HackerNoon profile picture

A little backstory on how this started. My team needed to save some on money on the infrastructure we were requesting and since most of the load for the application we wanted to build would be on the client-side rather than the service side. We decided to see if we could combine a Spring Application with a React app and serve up a single war file.

Find out how to combine Create React App with Spring Boot to give you a single war file 

The basic idea of what Spring Boot and Create React App do.

  1. Create React App helps you start a React project very quickly. It gives you all the basic things you need to get up and running asap.
  2. Spring boot helps you start and maintain a spring application quickly and easily.

Goals:

  1. Frontend and backend in a single war file with optimized productions build
  2. Keeping the benefits Create React App gives such as hot reloading

Setup:

  1. Must have java installed. Head over here to download a version
  2. Must have maven installed. For Mac, I used Homebrew (brew install maven )but otherwise, head here
  3. Must have node installed. For Mac, I used Homebrew (brew install node )but otherwise, head over here

* Side note: my IDE of choice is IntelliJ. When working on react code I usually switch over to VS Code. Feel Free to use what makes you feel comfortable

  1. Create an empty repo on Github and add a Read Me, gitignore, license, etc.
  2. Head over to https://start.spring.io to create your spring application and download locally. Group and Artifact can also be anything you want it to be.
  3. GroupId: e.the.awesome

    Artifact: spring-react-combo-app

3. Unzip the downloaded project into your git directory. Commit, Commit, Commit. Your SpringReactComboAppApplication should look like this.

package e.the.awesome.springreactcomboapp;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;

@SpringBootApplication
public class SpringReactComboAppApplication  extends SpringBootServletInitializer{

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

}

4. Let’s create a basic service now. We’ll call it the DadJokesController. This should be created in the same folder as SpringReactComboAppApplication file. I’m aware this is not a proper Rest API format but let’s ignore that for now.

package e.the.awesome.springreactcomboapp;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class DadJokesController {
    @GetMapping("/api/dadjokes")
    public String dadJokes() {
        return "Justice is a dish best served cold, if it were served warm it would be just water.";
    }
}

5. In your terminal run

mvn spring-boot:run

Then in your browser check http://localhost:8080/api/dadjokes . You should see the dad joke we added to our controller.

6. To create your React app, just run in the root directory

npx create-react-app basic-frontend-app

You can call it whatever you want, I just called mine basic-frontend-app

7. To run the front end application:

cd basic-frontend-app<br>npm start

After starting it should look like:

8. Since we want to integrate our Dad Jokes service into the frontend, first we will address proxy issues. If you’ve noticed, your service starts on localhost:8080 while the frontend starts on localhost:3000. If we try calling our service from the frontend, depending on your browser, you’ll get a CORS error.

The simplest way to address the issue is to have your front end proxy any requests from port 3000 to 8080. This change will be made in your package.json file

{
  "name": "basic-frontend-app",
  "version": "0.1.0",
  "private": true,
  "dependencies": {
    "react": "^16.3.1",
    "react-dom": "^16.3.1",
    "react-scripts": "1.1.4"
  },
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test --env=jsdom",
    "eject": "react-scripts eject"
  },
  "proxy": {
    "/api": {
      "target": "http://localhost:8080",
      "ws": true
    }
  }
}

Add the following to your frontend App.js file

import React, {Component} from 'react';
import logo from './logo.svg';
import './App.css';

class App extends Component {

    state = {};

        componentDidMount() {
            this.dadJokes()
        }

    dadJokes = () => {
        fetch('/api/dadjokes')
            .then(response => response.text())
            .then(message => {
                this.setState({message: message});
            });
    };

    render() {
        return (
            <div className="App">
            <header className="App-header">
            <img src={logo} className="App-logo" alt="logo"/>
            <h3 className="App-title">{this.state.message}</h3>
            </header>
            <p className="App-intro">
            To get started, edit <code>src/App.js</code> and save to reload.
        </p>
        </div>
    );
    }
}

export default App;

Restart the front end and you should be good. if you happen to get this error: I deleted my package-lock.json file and node_modules folder reinstalled npm packages and ran it again

9. Your application should now look like. You can see the results of the dad jokes API call.

10. Now that our basic front end and backend is complete, it’s time to create a production build and single war file.

Under the <dependencies> add this in

<!-- https://mvnrepository.com/artifact/com.github.eirslett/frontend-maven-plugin -->
<dependency>
<groupId>com.github.eirslett</groupId>
<artifactId>frontend-maven-plugin</artifactId>
<version>1.6</version>
</dependency>

Under the <plugins> section of the pom file, we will add the following commands which will do the following things when mvn clean install is run.

  1. npm install with the specified version of node
  2. run a production build of our frontend
  3. deposit the production build
  4. <plugin>
       <groupId>com.github.eirslett</groupId>
       <artifactId>frontend-maven-plugin</artifactId>
       <version>1.6</version>
       <configuration>
          <workingDirectory>basic-frontend-app</workingDirectory>
          <installDirectory>target</installDirectory>
       </configuration>
       <executions>
          <execution>
             <id>install node and npm</id>
             <goals>
                <goal>install-node-and-npm</goal>
             </goals>
             <configuration>
                <nodeVersion>v8.9.4</nodeVersion>
                <npmVersion>5.6.0</npmVersion>
             </configuration>
          </execution>
          <execution>
             <id>npm install</id>
             <goals>
                <goal>npm</goal>
             </goals>
             <configuration>
                <arguments>install</arguments>
             </configuration>
          </execution>
          <execution>
             <id>npm run build</id>
             <goals>
                <goal>npm</goal>
             </goals>
             <configuration>
                <arguments>run build</arguments>
             </configuration>
          </execution>
       </executions>
    </plugin>
    <plugin>
       <artifactId>maven-antrun-plugin</artifactId>
       <executions>
          <execution>
             <phase>generate-resources</phase>
             <configuration>
                <target>
                   <copy todir="${project.build.directory}/classes/public">
                      <fileset dir="${project.basedir}/basic-frontend-app/build"/>
                   </copy>
                </target>
             </configuration>
             <goals>
                <goal>run</goal>
             </goals>
          </execution>
       </executions>
    </plugin>

    Side note: order is important for your plugins so make sure your node/npm install execution is before copying the build file execution

11. After adding this, run mvn clean install and verify that the target/classes directory contains both frontend files and java files. And you should be good to go.

A final look at my pom file

So that’s all I got. If you would like to take a look at the repo or use it. It can be found here on my Github.

Next up is an article on how to deploy your war file on Heroku. Look forward to it!