A Lovely Spring View: Spring Boot & Vue.js

2 Comments

It´s time to shed some light on the integration of Vue.js with the popular Java Enterprise framework Spring Boot! Both frameworks are shining stars in their respective domain – but how could they be set up together properly? What is a practical project structure and build process? How does the development work locally and how is the app deployed? Let´s elucidate these questions!

Vue.js looks like a great choice!

I have to admit: I am not a frontend developer! I really like to play around with “backend stuff” like Spring Boot, web and microservices, automating whole infrastructures with Vagrant, Packer, Ansible and Docker – while I just wanted to scale my apps with Spring Cloud, Docker Swarm and Traefik

But wait! Isn´t there a frontend I need to build from time to time?! Yeah, there aren´t that many occasions, but sometimes I have to get my hands dirty with some JavaScript code. 🙂 Especially when you are giving lectures at university and try to digitize some old paper processes with your students. And if you ask our CTO, this “backend developer” thing won´t be around much longer…

spring boot vuejs wanted modern JavaScript framework meme

Alright then, we need to choose the “right” frontend framework – having nearly no clue. The last web app I built was based on Angular 1 – and it felt like a good choice! I loved the coding experience and after a day of training, I felt able to write awesome frontends… But now it’s two years later and I heard rumors that there was a complete rewrite of Angular (2), a new kid in town from Facebook (React) and lots of ES201x stuff, dependency managers, linters and more. Whew! But if I get my colleagues right, Vue.js isn´t the worst choice! Just take a look at all those posts that have been written in our blog this spring (especially if you need a deeper dive into Vue.js):

Rapid prototyping with Vue.js
Vue.js – it’s simple until you make it complicated
Vue.js & React – JavaScript UI Frameworks im Vergleich

Also, other projects move from one of the other big frameworks like Angular to Vue.js. Some of the main points are Vue.js’ much flatter learning curve and the higher coding speed compared to Angular and React. And the introduction phrase sounds really great:

Vue (pronounced /vjuː/, like view) is a progressive framework for building user interfaces. Unlike other monolithic frameworks, Vue is designed from the ground up to be incrementally adoptable. The core library is focused on the view layer only, and is very easy to pick up and integrate with other libraries or existing projects.

Project setup

So I think it could be a good idea to invest some time into Vue.js. As you may know, I strive to write quite comprehensible blog posts – you can find every bit inside the example project on GitHub. So let´s have a quick look into the project´s structure. The project uses Maven´s Multi Module capabilities to achieve a clean setup:

spring boot vuejs example projects structure

The pom.xml in the project´s root folder spring-boot-vuejs therefore contains the two modules backend and frontend:

    <modules>
        <module>frontend</module>
        <module>backend</module>
    </modules>

Spring Boot 2.0.x backend

The easiest way to create a new Spring Boot app is – as Josh Long really likes to emphasize – start dot spring dot io! Just initialize a Spring Boot app with the Web dependency and place the generated zip´s contents into the backend folder. There are only two things I had to change for the Vue.js integration. First the spring-boot-starter-parent has to move to our parent pom.xml in the root directory.

Second – and this is a key concept of our project setup here – we need to copy the generated HTML, JavaScript & CSS to a resources folder where it can be served by Spring Boot´s embedded Webserver later easily. This could be done with the maven-resources-plugin:

    <plugin>
      <artifactId>maven-resources-plugin</artifactId>
      <executions>
        <execution>
          <id>copy Vue.js frontend content</id>
          <phase>generate-resources</phase>
          <goals>
            <goal>copy-resources</goal>
          </goals>
          <configuration>
            <outputDirectory>src/main/resources/public</outputDirectory>
            <overwrite>true</overwrite>
            <resources>
              <resource>
                <directory>${project.parent.basedir}/frontend/target/dist</directory>
                <includes>
                  <include>static/</include>
                  <include>index.html</include>
                </includes>
              </resource>
            </resources>
          </configuration>
        </execution>
      </executions>
    </plugin>
  </plugins>

It just takes the results from the frontend build process (placed in /frontend/target/dist) and places them into /backend/src/main/resources/public folder. With this a simple java -jar backend-0.0.1-SNAPSHOT.jar will run our Spring Boot App and serve all the frontend assets. But first of all let´s build a frontend to serve!

Just for later needs we also create a simple RESTful Service in BackendController.java and use the great testing tooling from rest-assured together with Spring Boot to test our services inside the BackendControllerTest.class:

@RunWith(SpringRunner.class)
@SpringBootTest(
		classes = SpringBootVuejsApplication.class,
		webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT
)
public class BackendControllerTest {
 
	@LocalServerPort
	private int port;
 
	@Test
	public void saysHello() {
		when()
			.get("http://localhost:" + port + "/api/hello")
		.then()
			.statusCode(HttpStatus.SC_OK)
			.assertThat()
				.body(is(equalTo(BackendController.HELLO_TEXT)));
	}

Vue.js 2.5.x frontend

If you want to reproduce every step mentioned here, you´ll need a working Node.js installation as a prerequisite. Just use your preferred package manager and install it – like brew install node on a Mac. We´ll also need the Vue.js command line interface vue-cli, which could be installed with the help of npm, the Node.js Package Manager:

npm install --global vue-cli

To initialize a project skeleton for Vue.js, we only have to execute the following inside the frontend directory:

vue init webpack frontend

This command results in some questions that require answers:

spring boot vuejs vuecli init command resulting in questions

The generated skeleton is a great starting point for your Vue.js experiments. If you want to learn more about installing Vue.js, just head over to the docs.

The frontend-maven-plugin

The easiest way to handle every bit of the quite complex frontend build process with npm, node.js, ESLint, Karma, webpack and so on is to just use the frontend-maven-plugin. If you know Maven, that’s everything you need! Just add the plugin to the frontend’s pom.xml and you can use your well-known Maven commands:

<build>
    <plugins>
        <plugin>
            <groupId>com.github.eirslett</groupId>
            <artifactId>frontend-maven-plugin</artifactId>
            <version>${frontend-maven-plugin.version}</version>
            <executions>
                <!-- Install our node and npm version to run npm/node scripts-->
                <execution>
                    <id>install node and npm</id>
                    <goals>
                        <goal>install-node-and-npm</goal>
                    </goals>
                    <configuration>
                        <nodeVersion>v9.11.1</nodeVersion>
                    </configuration>
                </execution>
                <!-- Install all project dependencies -->
                <execution>
                    <id>npm install</id>
                    <goals>
                        <goal>npm</goal>
                    </goals>
                    <!-- optional: default phase is "generate-resources" -->
                    <phase>generate-resources</phase>
                    <!-- Optional configuration which provides for running any npm command -->
                    <configuration>
                        <arguments>install</arguments>
                    </configuration>
                </execution>
                <!-- Build and minify static files -->
                <execution>
                    <id>npm run build</id>
                    <goals>
                        <goal>npm</goal>
                    </goals>
                    <configuration>
                        <arguments>run build</arguments>
        </configuration>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>

The configuration of the frontend-maven-plugin ensures that the correct Node.js and npm versions are installed – with Node.js version > 4.0.0 the plugin takes care of the corresponding npm version, so you don´t have to explicitly configure it here. Additionally it installs all of our frontend dependencies (via npm install) and runs the full frontend build process with npm run build.

Tell webpack to output build contents to /target

The standard Node.js way is to create a dist directory for builds which contain the minified source code of our web application. But as we use Maven here, we need to have everything in the target directory. Therefore we need to change the generated frontend/config/index.js and replace the following lines:

index: path.resolve(__dirname, '../dist/index.html'),
assetsRoot: path.resolve(__dirname, '../dist'),

with

index: path.resolve(__dirname, '../target/dist/index.html'),
assetsRoot: path.resolve(__dirname, '../target/dist'),

First app run

Now we already have everything in place to fire up our Spring Boot powered Vue.js application! Just enter the project’s repository and let Maven do its job inside the root spring-boot-vuejs directory:

mvn clean install

This will build our whole app and we can simply do a:

java -jar backend/target/backend-0.0.1-SNAPSHOT.jar

Now let´s open our browser and hit http://localhost:8088/. That´s it, our app should now look like this:

spring boot vuejs first run example application

Development process

Although we now have our app successfully running for the first time, we need to take a closer look at how the development process could work with Spring Boot and Vue.js integrated. Because we don´t really want to wait for the full Maven/npm build to complete and wait for our app to be fired up with java -jar until we see a simple frontend element changed in the browser!

We want to get much faster feedback if we change frontend code. Luckily the webpack-dev-server comes to the rescue! This piece of magic will just update and build every change through all the parts of our JavaScript build chain instantly – and is pre-configured in our setup out of the box. The only thing to start the webpack-dev-server is to switch over to the frontend directory and run:

npm run dev

That’s it! Just try it yourself. The command automatically opens your browser. Just enter the Vue.js source in frontend/src and change something. It will instantly be visible 🙂

Another neat tool is the Vue.js browser extension. Just install it into your Chrome or Firefox and you should have a deep look into your Vue components inside the Browser´s development tools:

spring boot vuejs Vue.js Development extension in Chrome

Accessing Spring Boot REST services from Vue.js frontend

As we´re integrating Vue.js with Spring Boot, we for sure want to call some of those nice RESTful web services our Spring Boot backend provides us with. There are many libraries to access web services that one can use with Vue.js. One of them is axios, which is a quite popular Promise API based HTTP client with a huge number of GitHub stars. To use axios in our project, we only have to install the npm dependency:

npm install axios --save

Calling a REST service with Axios is simple. Go into the script area of your component (e.g. in the Service.vue) and add:

import axios from 'axios'
 
data () {
  return {
    response: [],
    errors: []
  }
},
 
callRestService () {
  axios.get(`/api/hello`)
    .then(response => {
      // JSON responses are automatically parsed.
      this.response = response.data
    })
    .catch(e => {
      this.errors.push(e)
    })
}
}

Now inside the template area of your Vue.js component you can request a service call with the callRestService() method – and access the response data accordingly:

<button class=”Search__button” @click="callRestService()">CALL Spring Boot REST backend service</button>
 
<h3>{{ response }}</h3>

Get out of the same-origin policy (SOP) hell

The HTTP calls with axios are working fine – except when we try to use them in our local fast feedback development process with the webpack-dev-server. Why? Because if we start the webpack-dev-server via npm run dev, it will serve our web application on http://localhost:8080. But our Spring Boot REST backend runs on http://localhost:8088! As a core concept of web application security, the same-origin policy (SOP) will then prevent our Vue.js frontend from accessing its Spring Boot backend – resulting in SOP errors.

One way to solve this problem is to use the Cross Origin Resource Sharing Protocol (CORS). Although this isn´t a big deal with both axios and Spring Boot, there´s a much more nifty solution. Thanks to my colleague Daniel who pointed me to the nice proxying feature of webpack-dev-server, we don´t need to configure all the complex CORS stuff!

According to the Vue.js Webpack Template the only thing we need to configure is a Proxy for our webpack-dev-server requests. This can easily be done in the frontend/config/index.js – right inside the dev.proxyTable field:

dev: {
    ...
    proxyTable: {
      // proxy all webpack dev-server requests starting with /api to our Spring Boot backend (localhost:8088)
      '/api': {
        target: 'http://localhost:8088',
        changeOrigin: true
      }
    },

With this configuration in place, the webpack-dev-server uses the really handy http-proxy-middleware to proxy all frontend requests with a preceding /api from http://localhost:8080 to http://localhost:8088 – including changing the Origin HTTP header accordingly. Remember that our Spring Boot REST services are configured to serve those requests to /api on class level:

@RestController()
@RequestMapping("/api")
public class BackendController {
 
    @RequestMapping(path = "/hello")
    public @ResponseBody String sayHello() {
        return "Hello from Spring Boot Backend!";
    }

The proxyTable configuration is finally used in the frontend/build/dev-server.js to configure the proxyMiddleware. We don´t need to change anything here, but I wanted to show the usage of the http-proxy-middleware:

// proxy api requests
Object.keys(proxyTable).forEach(function (context) {
  var options = proxyTable[context]
  if (typeof options === 'string') {
    options = { target: options }
  }
  app.use(proxyMiddleware(options.filter || context, options))
})

Is the app ready for deployment?

OK, now I can´t really conceal that I love that Continuous Delivery stuff. 🙂 So no example project is complete as long as it´s not running anywhere “productively”.

As you may already have found out, the example project has a live deployment configuration for Heroku: With every push into the master branch, our Spring Boot powered Vue.js app is built by TravisCI and deployed to:

https://spring-boot-vuejs.herokuapp.com

In the course of setting up the example project I really fell in love with the great Automatic deploys feature of Heroku and Heroku Pipelines. If the “Wait for CI to pass before deploy” checkbox is selected, we get a fully working pipeline and are able to demonstrate that our example project is also ready for deployment! Thanks again for pointing me to this great platform, Benedikt!

And that´s it!

Now we found a nice way to integrate Vue.js and Spring Boot. As both are really popular frameworks in their respective domain, I hope this introduction is helpful to some of you folks. If you manage to work through all the points shown in this post, you will have a solid project setup that can be used for both local development and deployment – mainly because both Vue.js and Spring Boot bring in such great features. And as “old school” Maven is supported by nearly every platform, this setup is ready for anything you can imagine. Let me know about your great Spring Boot powered Vue.js apps!

Jonas Hecht

Trying to bridge the gap between software architecture and hands on coding, Jonas hired at codecentric. He has deep knowledge in all kinds of enterprise software development, paired with passion for new technology. Connecting systems via integration frameworks Jonas learned to not only get the hang of technical challenges.

Kommentare

  • Joseph

    3. July 2018 von Joseph

    Nice job! I’ll try to convert this project using gradle instead of Maven :@

    • Jonas Hecht

      4. July 2018 von Jonas Hecht

      Thank you! Would be great to see your fork! I´am really interested what the Gradle variant would look like 🙂

Comment

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