There are numerous ways to integrate frontend code in Spring-Boot-based web applications. One of them was recently demonstrated by our blog post A Lovely Spring View: Spring Boot & Vue.js from my colleague Jonas Hecht.
In this blogpost you’ll learn a lean way to integrate frontend code in your Spring Boot app.
The problem
When integrating frontend code, we often have to deal with multiple things like: resources, HTML, CSS, JavaScript, Typescript, minification, etc. – often through the means of complicatedly generated build scripts which are difficult to debug.
I’ve been looking for a simple solution for quick experiments for quite a while now… then I stumbled upon ParcelJS, which solves a part of this by using convention over configuration.
ParcelJS is a simple web application bundler that packages your frontend code with sane defaults that do what you want – at least most of the time. Great for small and simple projects or demo apps.
In the following post I’ll describe how you can bundle and serve your frontend code from within a Spring Boot app without using any proxies, dedicated dev-servers or complicated build systems! And you’ll also get cool stuff like compression, minification and live-reload for free. 🙂
Sounds promising? Then keep reading!
For the impatient, you can find all the code on GitHub here: thomasdarimont/spring-boot-micro-frontend-example
Example application
The example application uses Maven and is composed of three modules wrapped in a fourth parent-module:
acme-example-apiacme-example-uiacme-example-appspring-boot-micro-frontend-example(parent)
The first module is acme-example-api, which contains the backend API which, in turn, is just a simple @RestController annotated Spring MVC Controller. Our second module acme-example-ui contains our frontend code and uses Maven in combination with Parcel to package the application bits. The next module acme-example-app hosts the actual Spring Boot app and wires the two other modules together. Finally, the spring-boot-starter-parent module serves as an aggregator module and provides default configuration.
The parent module
The parent module itself uses the spring-boot-starter-parent as parent and inherits some managed dependencies and default configuration.
The API module
The GreetingController class in the acme-example-api module:
The Maven build pom.xml is straightforward:
The APP module
The App class from the acme-example-app module starts the actual Spring Boot infrastructure:
For our app, we want to serve the frontend resources from within our Spring Boot app.
Therefore, we define the following ResourceHandler and ViewController definitions in WebMvcConfig in the acme-example-app module:
To make the example more realistic, we’ll use /acme as a custom context-path for our app via the application.yml in the
server:
servlet:
context-path: /acme
The Maven pom.xml of our acme-example-app module looks a bit more wordy as it pulls the other modules together:
The UI module
Now comes the interesting part: the acme-example-ui Maven module which contains our frontend code.
The pom.xml for the acme-example-ui module uses the com.github.eirslett:frontend-maven-plugin
Maven plugin to trigger standard frontend build tools, in this case node and yarn.
The “frontend” code resides in the directory /acme-example-ui/src/main/frontend and has the following structure:
└── frontend
├── index.html
├── main
│ └── main.js
└── style
└── main.css
The index.html contains just plain html that references our JavaScript code and assets:
The JavaScript code in main.js just calls our small GreetingController from before:
Note that I’m using ES7 syntax here.
The CSS in main.css is nothing fancy either…
Note that I’m using the “new” native CSS variable support, feels a bit otherworldly, but oh well.
Now to the climax of this “small” post, the package.json. In this small config we can find some helpful tricks:
In order to get support for ES7 features such as async JavaScript functions, we need to configure the babel transpiler via the file .babelrc.
The ParcelJS setup
We declare some scripts for clean,start,watch and build in order to be able to call them via `yarn` or `npm`.
The next trick is the configuration of parcel. Let’s look at a concrete example to see what’s going on here:
This line does several things:
--public-url ./
This instructsparcelto generate links relative to the path where we’ll serve the app resources from.-d target/classes/public
This tells Parcel to place the frontend artifacts in thetarget/classes/publicfolder where they… drumroll… can be found on the classpath 🙂src/main/frontend/index.html
The last part is to show Parcel where the entry point of our application is, in this casesrc/main/frontend/index.html. Note that you could define multiple entry points here.
The next trick is to combine this configuration with Parcel’s watch mode, which can be started via the parcel watch command.
As with many other web application bundler tools such as webpack, the watch allows to automatically and transparently recompile and repackage frontend artifacts whenever we change code.
So all we have to do to have a smooth frontend developer experience is to start a `yarn watch` process in the /acme-example-ui folder.
The generated resources will appear under target/classes/public and look like this:
$ yarn watch
yarn run v1.13.0
$ parcel watch --public-url ./ -d target/classes/public src/main/frontend/index.html
✨ Built in 585ms.
$ ll target/classes/public
total 592K
drwxr-xr-x. 2 tom tom 4,0K 8. Feb 22:59 ./
drwxr-xr-x. 3 tom tom 4,0K 8. Feb 22:59 ../
-rw-r--r--. 1 tom tom 525 8. Feb 23:02 index.html
-rw-r--r--. 1 tom tom 303K 8. Feb 23:02 main.0632549a.js
-rw-r--r--. 1 tom tom 253K 8. Feb 23:02 main.0632549a.map
-rw-r--r--. 1 tom tom 150 8. Feb 23:02 main.d4190f58.css
-rw-r--r--. 1 tom tom 9,5K 8. Feb 23:02 main.d4190f58.js
-rw-r--r--. 1 tom tom 3,6K 8. Feb 23:02 main.d4190f58.map
$ cat target/classes/public/index.html yields
The next trick is to just use Spring Boot devtools with Live-reload enabled. This will automatically reload the package contents if you touched any frontend code.
You can start the com.acme.app.App as a Spring Boot app and access the app by entering the URL http://localhost:8080/acme/app/ in your browser.
Adding Typescript to the mix
Now that we have our setup working, we might want to use Typescript instead of plain JavaScript. With Parcel this is quite easy.
Just add a new file to src/main/frontend/main with the name hello.ts
and reference it in the index.html file.
Since we’re running yarn watch, the parcel tool will figure out that we need a Typescript compiler based on the .ts file extension of our referenced file. Therefore ParcelJS will automatically add "typescript": "^3.3.3" to our devDependencies in the package.json file. That’s it!
Using less for CSS
We now might want to use less instead of plain css. Again, all we have to do here is rename main.css to main.less and refer to it in the index.html file via
ParcelJS will automatically add "less": "^3.9.0" to our devDependencies and provides you with a ready to use configuration that just works.
I don’t know about you, but this blew my mind when I saw it for the first time. Note that ParcelJS supports a lot of other asset types by default.
Once you are done with your app, you can just do a maven verify, which will automatically build your acme-example-api and acme-example-ui module and package it in the executable acme-example-app JAR.
Here is the tooling in action:
Next time you want to build something quick or just hack around a bit, then ParcelJS and Spring Boot might be a good fit for you.
More articles in this subject area
Discover exciting further topics and let the codecentric world inspire you.
Blog author
Thomas Darimont
Do you still have questions? Just send me a message.
Do you still have questions? Just send me a message.