Beliebte Suchanfragen

Cloud Native

DevOps

IT-Security

Agile Methoden

Java

//

A Lovely Spring View: Spring Boot & Vue.js

23.4.2018 | 10 minutes of reading time

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…

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:

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

1<modules>
2        <module>frontend</module>
3        <module>backend</module>
4    </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 :

1<plugin>
2      <artifactId>maven-resources-plugin</artifactId>
3      <executions>
4        <execution>
5          <id>copy Vue.js frontend content</id>
6          <phase>generate-resources</phase>
7          <goals>
8            <goal>copy-resources</goal>
9          </goals>
10          <configuration>
11            <outputDirectory>src/main/resources/public</outputDirectory>
12            <overwrite>true</overwrite>
13            <resources>
14              <resource>
15                <directory>${project.parent.basedir}/frontend/target/dist</directory>
16                <includes>
17                  <include>static/</include>
18                  <include>index.html</include>
19                </includes>
20              </resource>
21            </resources>
22          </configuration>
23        </execution>
24      </executions>
25    </plugin>
26  </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 :

1@RunWith(SpringRunner.class)
2@SpringBootTest(
3        classes = SpringBootVuejsApplication.class,
4        webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT
5)
6public class BackendControllerTest {
7 
8    @LocalServerPort
9    private int port;
10 
11    @Test
12    public void saysHello() {
13        when()
14            .get("http://localhost:" + port + "/api/hello")
15        .then()
16            .statusCode(HttpStatus.SC_OK)
17            .assertThat()
18                .body(is(equalTo(BackendController.HELLO_TEXT)));
19    }

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 :

1npm install --global vue-cli

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

1vue init webpack frontend

This command results in some questions that require answers:

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:

1<build>
2    <plugins>
3        <plugin>
4            <groupId>com.github.eirslett</groupId>
5            <artifactId>frontend-maven-plugin</artifactId>
6            <version>${frontend-maven-plugin.version}</version>
7            <executions>
8                <!-- Install our node and npm version to run npm/node scripts-->
9                <execution>
10                    <id>install node and npm</id>
11                    <goals>
12                        <goal>install-node-and-npm</goal>
13                    </goals>
14                    <configuration>
15                        <nodeVersion>v9.11.1</nodeVersion>
16                    </configuration>
17                </execution>
18                <!-- Install all project dependencies -->
19                <execution>
20                    <id>npm install</id>
21                    <goals>
22                        <goal>npm</goal>
23                    </goals>
24                    <!-- optional: default phase is "generate-resources" -->
25                    <phase>generate-resources</phase>
26                    <!-- Optional configuration which provides for running any npm command -->
27                    <configuration>
28                        <arguments>install</arguments>
29                    </configuration>
30                </execution>
31                <!-- Build and minify static files -->
32                <execution>
33                    <id>npm run build</id>
34                    <goals>
35                        <goal>npm</goal>
36                    </goals>
37                    <configuration>
38                        <arguments>run build</arguments>
39        </configuration>
40                </execution>
41            </executions>
42        </plugin>
43    </plugins>
44</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:

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

with

1index: path.resolve(__dirname, '../target/dist/index.html'),
2assetsRoot: 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:

1mvn clean install

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

1java -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:

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:

1npm 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:

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:

1npm 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:

1import axios from 'axios'
2 
3data () {
4  return {
5    response: [],
6    errors: []
7  }
8},
9 
10callRestService () {
11  axios.get(`/api/hello`)
12    .then(response => {
13      // JSON responses are automatically parsed.
14      this.response = response.data
15    })
16    .catch(e => {
17      this.errors.push(e)
18    })
19}
20}

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:

1<button class=”Search__button” @click="callRestService()">CALL Spring Boot REST backend service</button>
2 
3<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:

1dev: {
2    ...
3    proxyTable: {
4      // proxy all webpack dev-server requests starting with /api to our Spring Boot backend (localhost:8088)
5      '/api': {
6        target: 'http://localhost:8088',
7        changeOrigin: true
8      }
9    },

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:

1@RestController()
2@RequestMapping("/api")
3public class BackendController {
4 
5    @RequestMapping(path = "/hello")
6    public @ResponseBody String sayHello() {
7        return "Hello from Spring Boot Backend!";
8    }
1// proxy api requests
2Object.keys(proxyTable).forEach(function (context) {
3  var options = proxyTable[context]
4  if (typeof options === 'string') {
5    options = { target: options }
6  }
7  app.use(proxyMiddleware(options.filter || context, options))
8})

share post

Likes

3

//

More articles in this subject area

Discover exciting further topics and let the codecentric world inspire you.

//

Gemeinsam bessere Projekte umsetzen.

Wir helfen deinem Unternehmen.

Du stehst vor einer großen IT-Herausforderung? Wir sorgen für eine maßgeschneiderte Unterstützung. Informiere dich jetzt.

Hilf uns, noch besser zu werden.

Wir sind immer auf der Suche nach neuen Talenten. Auch für dich ist die passende Stelle dabei.