How to Setup Spring-Boot with ReactJS and Webpack

Benjamin Liu
8 min readDec 7, 2017

--

In the past, I’ve ran into many situations where I wanted to quickly spin up some lightweight services built using Spring-Boot that also took advantage of ReactJS and real component-based front end design. If you don’t want to read the tutorial and just want to use the skeleton.

https://github.com/liuben10/spring-boot-react-js-example

Motivation

Why use Spring-Boot? For me, it’s personally been the wealth of plugins and and neat little packages that actually makes development really streamlined and easy. For instance, all the spring-data-* packages are really useful when it comes to connecting with various database technologies; spring-security and spring-integration have also proven to be really useful as well in the past.

Why use ReactJS? ReactJS’s component-based design really does a good job encouraging code reusability as well as just overall readability. It also really does a good job of keeping front end assets together so as to improve navigating a large project.

Prerequisites

In this tutorial, I am using

  • npm 4.6.1
  • jdk1.8.0_92
  • git

For my personal development environment, I used IntelliJ IDE, but you can use whatever editor you want, although IntelliJ makes spinning up a new Spring-Boot project really easy.

Tutorial

The corresponding repository, which contains the final implementation is here. https://github.com/liuben10/spring-boot-react-js-example.

If you want to see how the project was built, you can follow it step by step by using git log, and then git checkout <commit_id> for each of the commits and going through the history of changes. Otherwise, I will go through the setup from scratch.

  1. Setup a simple Spring boot project.

There are many ways to setup a Spring Boot Project. I will first go over the initializer way, as well as doing it manually.

Using an Initializer

Navigate to https://start.spring.io/, Enter in your group and artifact name. Also, add Web as a dependency and click “Generate Project” to download a zip with your artifact-name as the directory name. Move this directory to wherever you want to have your spring boot project reside.

Doing it manually.

Create a new directory, presumably one that matches your artifact name. For this tutorial, we’ll just assume there’s a directory called ~/spring-boot-with-reactjs, and our artifact ID is spring-boot-with-react-js

Copy this into a pom.xml at the root level

<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>com.example</groupId>
<artifactId>spring-boot-with-react-js</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>

<name>artifact</name>
<description>Demo project for Spring Boot</description>

<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.9.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>

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

<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</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-web</artifactId>
</dependency>
</dependencies>

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


</project>

Create these directories, src/main/java as well as src/main/resources (create all the directories on the path if need be).

In your src/main/java, add

package com.example.springbootwithreactjs;

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

@SpringBootApplication
public class SpringBootWithReactJsApplication {

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

(Or with your own custom class name). Important things to note about this application: @SpringBootApplication is an annotation that pretty much declares that your class will be the entry point for your spring boot server as well as declaring it as a bean provider. More information can be found here https://docs.spring.io/spring-boot/docs/current/reference/html/using-boot-using-springbootapplication-annotation.html

Then, in your project root directory (same level as the pom.xml), run mvn install. This should generate a bunch of logging and then pull whatever dependencies are needed to build the artifact and then builds and installs the artifact. You should see a target directory get generated containing compiled resources.

To run the application server, in your root directory, run

mvn spring-boot:run

This should start the server by default on port 8080. If you want to customize the port, you can specify a parameter using -Dserver.port=8080, or in src/main/resources, add an application.properties file with server.port=8080 as the first line.

To verify that the server is up and running, you should go to http://localhost:8080 and verify that you will land on the white label error page like so:

This just means we need to add some controller methods.

2. Adding a Controller.

Now, we need to add a controller which basically specifies routes to the Dispatcher Servlet (the process responsible for routing http requests to actions).

We are going to add a PongController which will route requests with the URI=(“/pong”) to return a plain text response that just says “pong”

package com.example.springbootwithreactjs.controller;

import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

/**
*/
@RestController
public class PongController {

@RequestMapping(value = "/pong", method = RequestMethod.GET)
@CrossOrigin(origins = "*") //TODO change me!
public String pong() {
return "pong";
}
}

Important annotations to note:

@RestController is an important annotation in Spring because it declares that this class is a controller and therefore should be picked up by the spring boot application and loaded as a singleton-bean at application start time.

@RequestMapping basically defines that this method should be matched to a particular URI path when there is a GET request.

@CrossOrigin sets up CORS, otherwise, the server will deny if the referrer is something other than itself (which is important for us since we are going to be talking to the server from a front-end app). Alternatively, you could setup CORS definitions in one place by creating a WebMvcConfigurer, more detail is here. https://spring.io/guides/gs/rest-service-cors/

Verify that this works by hitting http://localhost:8080/pong; you should now see that there is a response that just says “pong”.

3. Setup the ReactJS app.

First, you need the facebook create-react-app utility. Run this command to install it globally (note you need npm from the prerequisite).

npm install -g create-react-app

Verify it works by running create-react-app.

Then, run

create-react-app <app_name>

This will then create a react front end app which you must start separately from your backend server. Verify that the installation works by cd-ing into your newly created front end app, and running yarn start you should see that the app is started at localhost:3000. Try changing some text in App.jsand verifying that the changes will dynamically get loaded into your browser.

4. Containerize your app (Note: this is optional, but I recommend following these steps so that you can better organize the react code)

The real advantage of React is being able to create reusable containers. In this part, we’re going to move the code out of App.js and into a container called Main.js which will hold our primary app state.

cd into your front end directory (<app_name> from the create-react-app step above), create src/container/Main.js (create any subdirectories if need be).

Move logo.svg into a subdirectory under src called assets, create the directory assets if need be.

Similarly, create a subdirectory under src called css, and move App.css into that directory.

In your Main.js add this:

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

class Main extends Component {
render() {
return (
<div className="Main">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<h1 className="App-title">Welcome to React</h1>
</header>
<p className="App-intro">
To get started, edit <code>src/containers/Main.js</code> and save to reload.
</p>
</div>
);
}
}

export default Main;

Then, replace your App.js with this:

import React, { Component } from 'react';
import Main from './container/Main';
import './css/App.css';

class App extends Component {

render() {
return (
<div className="App">
<Main />
</div>
);
}
}

export default App;

All this does is it moves the old code out of App.js and into Main.js so functionally nothing changed. However, now you have a project structure where not everything is just in one directory, and you can also set Main view specific state in the Main container rather than having a single state variable for the entire app.

5. Add a ping action.

Now, we should modify Main.js to have a button such that when we press the button, it will log in the developer console that it has been clicked.

In Main.js, add the function ping like this:

ping() {
console.log("Ping was clicked");
}

And replace the render method like this:

render() {
return (
<div className="Main">
<header className="App-header">
<h1 className="App-title">Ping</h1>
</header>
<p className="App-intro">
<div>
<button onClick={this.ping}>Ping!</button>
</div>
</p>
</div>
);
}

So the end result looks like this:

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

class Main extends Component {

ping() {
console.log("Ping was clicked");
}

render() {
return (
<div className="Main">
<header className="App-header">
<h1 className="App-title">Ping</h1>
</header>
<p className="App-intro">
<div>
<button onClick={this.ping}>Ping!</button>
</div>
</p>
</div>
);
}
}

export default Main;

Verify that this works by navigating to http://localhost:3000, opening up developer console via CMD + I or navigating to View > Developer > Developer Tools > Console

Then, hit the button and see that Ping was clicked. It should look something like this:

Ignore the warnings in my screenshot, the main thing is Ping was clicked shows up in the console logs.

6. Have the front end talk to the backend.

In this part, we are going to use a ReactJS ajax client to talk to our backend PongController endpoint that we previously defined. First off, ensure the server is still running on http://localhost:8080 by navigating to the url and verifying that you are landing on the Whitelabel error page. Alternatively, you can run lsof -i :8080 to see that the process is still running on port 8080.

Then, run npm install axios --save to save axios to your package.json. Then, in Main.js, add the line import axios from 'axios' to import axios into your project. Then modify the ping method in the Main component to invoke a get request to the PongController like so

ping() {
axios.get("http://localhost:8080/pong").then(res => {
alert("Received Successful response from server!");
}, err => {
alert("Server rejected response with: " + err);
});
}

If everything is setup properly, you should see “Received Successful response from server!” alert when you hit the ping button.

7. Change so that the Ping request updates the state of the application.

This is optional, but useful for showing how state can be managed in ReactJS components. In this example, we are going to update a label field to say “Ponged” when a successful pong callback was registered after the axios request.

In Main.js, add a constructor method like so:

constructor(props) {
super(props);
this.state = {ponged: 'Not Ponged'}

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

And then in the render method add a label like this:

render() {
return (
<div className="Main">
<header className="App-header">
<h1 className="App-title">Ping</h1>
</header>
<p className="App-intro">
<div>
<button onClick={this.ping}>Ping!</button>
<div>Ponged: {this.state.ponged}</div>
</div>
</p>
</div>
);
}

What this does is it declares a state object for Main.js as well as a state variable called ponged which defaults to ‘Not Ponged’. Note, the this.ping.bind(this) line. This is important for the next step since it will register the ‘this’ keyword to the ping function context. Allowing you to use ‘this’ to reference the component.

Now, modify the ping() method so that it will update the component state

ping() {
axios.get("http://localhost:8080/pong").then(res => {
alert("Received Successful response from server!");
this.setState({ponged: 'Ponged! '});
}, err => {
alert("Server rejected response with: " + err);
});
}

Now, what will happen is if you successfully get a response from the server, you should see the label change to ponged! like so.

The final product should look like this:

Now, you’ve successfully built an end to end application that consists of both a front-end app as well as backend app as well as successfully connected both of them together!

Feedback

So that basically concludes the tutorial. Any questions or comments, feel free to email me at liuben10@gmail.com.

--

--