Spring Boot with Kotlin bundled with a React application as a single deployable artifact

In this post we will cover a simple way to get started with developing a full-stack application using Spring Boot 2 with Kotlin, that builds and bundles a React-application and serves it statically. The react application will use create-react-app for a painful way to setup and develop a SPA (single page application).

tl;dr: You’re not interested in the details and you just want to get started? Clone this repo and get coding, and disregard the rest of this blog post.

Using Spring Initializr to get started with Spring Boot

The quickest and easiest way to get started with a Spring Boot application is to use Spring Initializr. Let’s go through the following options.

Whether or not you want to use Gradle or Maven to build this project is up to you, but in this tutorial we will use Gradle.

Select Kotlin as a programming language.

Group is your unique identifier, if you’re a private person this can be your domain in reverse, or if you don’t have a domain you can use your GitHub account on the form of com.github.yourusername. My domain is karl.run so I’ll use run.karl.

Artifact is the name of your application.

Dependencies is the Spring Boot features you’d like to start with. Don’t worry, you can add these later, but we will start of with “Web” and “DevTools” that’ll give us what we need to start.

Hit Generate project and download the zip.

Creating a module structure

There are many ways to structure your applications. In this case we will set up a completely separate React application that only uses the Spring Boot server as an API. However, to simplify deployment we want the Spring Boot server to statically serve the React application for us. That way we only have to deploy a single artefact (a .jar file) as a single server.

The route we will take here is to keep the two modules (React and Spring Boot) as separate as possible, and only join them together when we create our production build.

Set up the skeleton

Create a new folder with your project name, for example spring-react. Create a folder inside this called server and unzip the contents of the zip from Spring Initializr into it. We should then have a folder structure that looks like this:

You can go ahead and cd into server and run ./gradlew build to verify that the Spring Boot build from Spring Initializr has been set up correctly. If everything is fine you should see a BUILD SUCCESSFUL after a few seconds.

Now create a new folder next to server and call it web. Next make sure you have installed Node, to verify that you have installed this correcly you should be able to do node -v and npm -v and see its version. Make sure it is relatively new (the LTS version should be enough). Once Node is installed, with spring-react as your working directory, run:

Now that we have our two modules where we want them, we can start them at the same time.

Inside server run:./gradlew bootRun

Inside web run:npm start

This will start our backend Spring Boot application on localhost:8080 and our frontend React application on localhost:3000, and you cat visit these URLs in your browser, although none of them will do anything particularly interesting yet.

Making them talk together

Now starts the fun part. For this post we will make a simple REST-endpoint that returns some JSON and make it available on /api/hello. We will then make our React application request the data from this endpoint and display it.

The only change here is that we added a constructor that accepts the service (you can have multiple arguments in this constructor), and the constructor is annotated with @Autowired, meaning that Spring Boot will handle instantiating and passing these classes to our controller.

If we run the application and visit /api/hello again we will see {"message":"Hello World!"}. Note that this magically became a JSON-object because we returned a data class instead of a string. Now we are ready to consume this from our React-application!

Requesting from React

Now that we have our backend up and running, let’s jump into our React-application.

To make development a lot easier, first we need to tell create-react-app to proxy all our /api requests to our Spring Boot development server. In web/package.json, we add a the following property to the end of the file:

"proxy": "http://localhost:8080"

Now when we make a request from our React-application to a relative path such as /api/hello it will be proxied to the running Spring Boot application on a different port! Very useful.

Now, let’s make sure we run both our backend and our frontend and the same time, but separately Run the backend with ./gradlew bootRun from the server folder. If you prefer, configure your IDE to execute the Spring Boot application (very useful for debugging). Now in a separate terminal go to the web folder and run npm start.

Visit http://localhost:3000 to see the default create-react-app page.

Now let’s make a request to our backend! In web/App.js, let’s add a state to store our response, as well as add a life-cycle method that will be executed when the component is mounted, which will fetch the message from the backend.

classAppextendsComponent{
state ={
message:null};componentDidMount(){// Make a GET-request to our backendfetch("/api/hello")// Map the result to a JSON-object.then(response => response.json())// Put the message in the state.then(response =>{this.setState({ message: response.message });});}render(){return(<divclassName="App"><p>Message from backend:{this.state.message}</p></div>);}}

Now your React-application should already have hot-reloaded, made a request to the backend and displayed it. If you get a error message with Proxy error: Could not proxy request /api/hello in the terminal, it means you either aren’t running your Spring Boot application at the same time, or that you have the wrong URL in your proxy-property in package.json.

Now that you have your backend and your frontend communicating, create your web-application!

Packing it all together

When you want to ship your web-application, you need to pack it into an artefact that you can deploy somewhere. There are a plethora of different ways to do this, but in this tutorial we will build the React-application, copy the files into the Spring Boot application as static files, then build the Spring Boot application into a JAR-file. That file, we can deploy anywhere that supports the JVM, or package it into a Docker-container and make it even more generic.

Build the frontend

create-react-app has already done all the heavy lifting for us, simply running npm build creates a nicely minified, production-ready build. The only thing we need to do is move the files in web/build/ into server/src/main/resources/static. We want to do that automatically every time npm build is executed.

To do that, we are going to add two useful npm-modules, but only as dev-dependencies.

npm add -D copyfiles cross-env.

Then under scripts, we add two new script-targets, one that tests and builds our React-application and one that copies our files to the correct location.

We name it build:gradle because we want to keep our normal build-script, and :gradle indicates that we’ll usually run this from our gradle-script in the other module. By naming the other script-target postbuild it will automatically execute any time we run build.

Try our new build and copy script by running npm run build:gradle in the web-folder.

Any application should be able to be build scratch using a single command, that means that we need to configure our backend to automatically run the frontend build as well.

In server/build.gradle we need to add a gradle-plugin that lets us invoke npm commands easily. Under buildscript/dependencies, add the following dependency:

classpath("com.moowork.gradle:gradle-node-plugin:1.2.0")

Apply the plugin next to the other plugins: apply plugin: 'com.moowork.node'.

Now we add two tasks at the bottom of our build.gradle. One task will install the frontend dependencies, the other will execute build:gradle from our package.json. Then we configure gradle so that any time ./gradlew build is executed, it first builds the frontend application, before packing that and Spring Boot application together to one JAR-file.

Simple! No magic here. Just a gradle plugin that helps us to execute npm from gradle, and some simple task dependsOn chaining.

To check that everything works as expected, run ./gradlew build from the server-folder. If you see BUILD SUCCESSFUL, try running your production-ready JAR-file directly, which you can do from the server-folder with java -jar build/libs/server-0.0.1-SNAPSHOT.jar.

Visit http://localhost:8080 to see your Spring Boot hosted React-application that communicates with a Spring Boot REST-API.

Extra tip: Making Spring Boot behave when serving a single page application (SPA)

SPAs prefer to handle their routing self. Which means that if you build a React-application that uses React Router or Reach Router, and you don’t want /#/ part of your URL, you need to configure Spring Boot to route any non-root (/) route back to root.

Luckily that can be fixed with this little workaround. Next to Application.kt create a new file called WebConfig.kt.

A Spek-test consists of a root-describe, which can have more describes nested under itself, and/or one to many it. The it blocks are where the actual assertions will take place. Build a logical structure that builds natural flowing sentences. For example

object ExampleControllerSpec :Spek({describe("a very good controller"){// Create a mock of ExampleServiceval mockedExampleService = mock<ExampleService>{/* Define that when getSomeValue is invoked, return this
value instead of executing the original code */
on {getSomeValue()} doReturn ExampleService.ExampleResponse("Mocked Message Wahoo!")}/* Specify that we want to use our mocked
ExampleService by passing it in as a named parameter */val controller =ExampleController(exampleService = mockedExampleService)it("should return invoke service but return mocked message"){val value = controller.example()
assert.that(value.message,equalTo("Mocked Message Wahoo!")andendsWith("Wahoo!"))}}})

Using mockito-kotlin to define behaviours of our mocked classes is very natural and quick. Follow the comments in the above example.

Remember that this tutorial is available as a ready to use template on github/karl-run.

If you find anything wrong in this blog post, don’t hesitate to leave a comment or send me a mail at karl@karl.run. I’m always happy for feedback!