React + Quarkus integration using Maven


Introduction

In this post I will show you how to serve a ReactJS (or any other JavaScript SPA) front-end application using a Java Quarkus Application as a back-end and static page server. The post also includes instructions on how to configure Maven to perform the front-end build tasks along with your application packaging.

This article is very similar to my 2017 Angular + Spring Boot integration using Gradle blog post, with some added features to enable React’s router to work.

Requirements

The technology stack required for this example is:

The main goal is to achieve an npm install + build that integrates with the Maven resources plugin and deploys the distribution files to a directory where Quarkus can serve them. Quarkus will then act both as the back-end service and the gateway API to serve the front-end too.

You can find the code for these configurations in my YAKC – Kubernetes Dashboard quickstart application for YAKC (Yet Another Kubernetes Client).

Back-end (Quarkus)

In this new Microservice world, it makes sense that the front-end and back-end(s) are different services that are usually served and orchestrated by a gateway API. It’s hard to find those former web applications where front-end and back-end were part of the same service.

An image showing a microservice vs a monolith

This distributed architecture makes sense for large enterprise projects, but sometimes we just want to prototype or quickly build a simple web application. That’s the time when you start missing those old monoliths and their simplicity that allowed you to quickly create and especially deploy your application.

The main goal is to achieve that Quarkus acts as a gateway API. So in addition to provide the REST API, it serves the front-end files too. This way we get to keep ReactJS as our main front-end technology, Quarkus as our back-end, and avoid all of the extra complexities needed for a 2 (or more) separate service deployment.

You may think that Quarkus already provides a way to serve static resources and that the solution is as simple as storing the front-end built files in this directory before building the back-end. However, this will not allow the React router to work and you wouldn’t be able to route /nested/front-end/routed/paths (Just try to refresh your browser in one of those nested paths).

GatewayResource class

This class is the main entry point for any HTTP request that the application receives.

The first method, getApiResource delegates any request to /api/v1 to the ApiResource subresource instance that will take care of it.

The getFrontendRoot will take care of any request to the root path /by requesting the index.html file to the getFrontendStaticFile which I’ll describe next.

Finally the getFrontendStaticFile will take care of the rest of the requests. The method first tries to locate any file with the provided file name in the /frontend resource directory. This directory should be packaged during the application build phase and contains the ReactJS built application.

If no file is found with this name, it will serve the index.html file. This is the part that allows the React router to work. Any request not matched either by the API subresource or a front-end file will serve the React application which will then take care of performing a “front-end” routing.

Front-end (React)

Your complete front-end application may be located anywhere within your project, in my case I’ve located it under src/main/frontend. Besides noting the path location for your React application, you don’t really need any extra configuration for this procedure to work.

Maven build

For the final step to integrate the React front-end with the Quarkus back-end you need to tune the Maven build.

Resources

In this first excerpt, I’ve added a new entry to the build resources configuration. Besides the standard src/main/resources, the directory where the React application is built src/main/frontend/build will also be assembled and copied to target/classes/frontend directory. If you recall, in the GatewayResource class, I was loading static resources from this directory.

Npm build + install

The second excerpt is used to run npm install and npm run build from Maven. I added this as a separate profile because I usually perform these steps manually while in my local machine. I only invoke them in the CI, so my CI build step looks like mvn -Pbuild-frontend clean package. However, there’s no problem in adding these configurations directly to your pom.xml build section.

As you can see I configured 2 executions for the Exec Maven Plugin. The first one will invoke npm install using the front-end home directory (src/main/frontend) as its working directory. The second execution has a similar configuration but runs npm run build instead.

Execution

Once your application is packaged, you can run it either by running the generated jar (java -jar ./target...) file or by running mvn quarkus:dev.

A recording of how front-end routing works with Quarkus

Development mode

If you want to enjoy and benefit from both Quarkus and React hot/live reloading you will need some extra configuration.

In this case, we will run the front-end using the standard npm start and pointing our browser to http://localhost:3000. And we’ll run our back-end using mvn quarkus:dev in http://localhost:8080.

Back-end

Since the front-end is served from a different URL, we need to enable CORS in Quarkus.

We can do this by modifying our application.properties to include:

These properties enable CORS to allow requests from the front-end dev origin. You can modify the entries to make it more or less restrictive depending on your needs.

Front-end

For the front-end part, we need to tune our React application to hit different back-end endpoints depending on the environment we’re running it on.

In my case, since I’m using create-react-app, I added two environment property files:

.env for production:

.env.development for dev:

Once these two files are ready, create-react-app takes care of the appropriate replacements in your application depending on your target environment. In order to use those values, you’ll need to reference the URL using the process.env.REACT_APP_API_URL variable:

Conclusion

In this post, I showed you how to serve both a JavaScript front-end and a Java back-end application using Quarkus. Quarkus acts as an API gateway and either serves the front-end static files or processes the back-end request using resources and services. I’ve also shown you how to combine and use hot reload in React and Quarkus by tuning the settings for each application.

I’ve extracted the code for this post from my YAKC – Kubernetes Dashboard quickstart. You can learn more by visiting  YAKC’s GitHub project site. You can also see related code here.

Leave a comment

Your email address will not be published. Required fields are marked *