Categories: AngularSpring Boot

Integrate Angular with a Spring Boot project

Developing a multi-module application where the backend runs on Spring Boot and the frontend is powered by Angular is far less complicated than one might expect. Check out how you can build such a project and manage it as a single jar file.

What we are going to build

The goal is to design and create a multi-module application, established with frameworks: Spring Boot for the backend and Angular for the frontend. After that we will build the project into a single jar file using Maven.

The finished project is available in the following GitHub repository: little-pinecone/spring-boot-angular-scaffolding.

Requirements

I try to keep the project up to date. Visit the releases list to keep track of the current versions of frameworks and libraries used.
  • We are working on a fresh Spring Boot project with the Web package dependency. You  can read about setting up this project with Spring Initializr in How to create a new Spring Boot Project. In this tutorial I named the Spring Boot project spring-boot-angular-scaffolding.
  • Angular CLI – a command line interface tool that generates a project as well as performs many development tasks.
    I’m working on:
    $ ng --version
     
    Angular CLI: 6.0.8
    Node: 8.11.3
    OS: linux x64
    Angular: 6.1.1
  • This project was created on Kubuntu (Ubuntu 18.04 LTS) – remember to adjust commands if you don’t work on Linux OS.
  • For the IDE I recommend IntelliJ IDEA Community Edition.

Architecture overview

In our multi-module maven project we want all components to be built together and share some metadata, but to differ in configuration. Fortunately, we can specify a parent project in both modules and define submodules in the parent – we will utilize both Project Inheritance and Aggregation.

The final project directory tree:

Manage the Spring Boot module structure

We will start with preparing the backend module. Go to the main folder of the Spring Boot app:

$ pwd
/home/little_pinecone/projects/spring_boot_with_angular/spring-boot-angular-scaffolding

Create the directory where the backend part of our app will be stored:

$ mkdir backend

Now, move the src directory to the freshly created backend folder:

$ mv src backend

We also need a child pom.xml file to specify Spring Boot dependencies, so copy the already existing file from the parent project to our backend directory:

$ cp pom.xml backend

In pom.xml reserved for the Spring Boot module we will leave all required maven dependencies unchanged – the application will look for them here.

However, we will completely alter the content of the <parent> element:

  • to specify which artifact is the parent for this pom we will use the fully qualified artifact name of the parent pom – spring-boot-angular-scaffolding;
  • to keep the groupId and the version of the module the same as its parent, we will enclose those fields here.

Furthermore, there are some metadata that require to be updated, namely:

  • <artifactId> – we will put backend here,
  • <name> – we will put backend here,
  • <description> – we will put The backend module built with Spring Boot here,
  • <properties> – remove it,
  • <packaging> – remove it.

Thanks to those changes the backend can be later recognised by the parent application as its module.

Please, double check which pom.xml you are editing – now we are working on spring-boot-angular-scaffolding/backend/pom.xml.

Leave the rest of the properties unaltered and check out the updated values:

<!-- backend/pom.xml -->
…
<artifactId>backend</artifactId>

<name>backend</name>
<description>The backend module built with Spring Boot</description>
…
<parent>
    <groupId>in.keepgrowing</groupId>
    <artifactId>spring-boot-angular-scaffolding</artifactId>
    <version>0.0.1-SNAPSHOT</version>
</parent>
…
<dependencies>
    …
</dependencies>
<build>
    …
</build>

Initialize a git repository in the main project:

$ git init

We will use the .gitignore file from the parent project, copy the file to the backend folder:

$ cp .gitignore backend

We’ve just prepared the backend module within our project – it won’t work as intended yet, we need to create the frontend module, and, later, bring all components together.

The work done in this section is contained in the commit ed4c7fe5127deaeffc81fe1012d9bca7a7038cee.

Manage the Angular module structure

In the parent project create the frontend/src/main/ folder for the web application sources – the src and main directories are here to follow the Maven standard directory layout. Use -p (–parents) option to create all non-existing parent directories:

$ mkdir -p frontend/src/main/

Similar to the backend, this module also needs its own pom.xml so copy the one prepared in the backend:

$ cp backend/pom.xml frontend

Update the following elements:

  • <artifactId> – we will put frontend here,
  • <name> – we will putfrontend here,
  • <description> – we will put The frontend module built with Angular here,
  • <dependencies> – remove it,
  • <plugins> – remove the following plugin:
    <plugin>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-maven-plugin</artifactId>
    </plugin>
Please, double check which pom.xml you are editing – now we are working on spring-boot-angular-scaffolding/frontend/pom.xml.

Check out the updated file:

<!-- frontend/pom.xml -->
…
<artifactId>frontend</artifactId>

<name>frontend</name>
<description>The frontend module built with Angular</description>

<parent>
   …
</parent>
<build>
   <plugins></plugins>
</build>

We will use the .gitignore file from the parent project, copy the file to the frontend folder:

$ cp .gitignore frontend

You can adjust the .gitignore to your needs or use the standard one created automatically by Angular CLI – in both cases remember to include the /target/ folder.

The frontend module is prepared but still empty, we will come back to it after creating the Angular project. For now, let’s move to aggregating the created modules with the parent application.

The work done in this section is contained in the commit cfcf3aad897d9adca9aa81c50e77e8bf25073074.

Tie the modules together

The backend and frontend pom.xml files contains the <parent> field so the modules know from which project they inherit. Now the main application requires data to identify which submodules should be aggregated during the build. We will provide them by updating the main pom.xml file by adding a new field – <modules> – with directories of its modules specified: backend and fronted.

Update the following properties:

  • <packaging> – we will put pom here,
  • <dependencies> – remove it,
  • <build> – remove it.
Please, double check which pom.xml you are editing – now we are working on spring-boot-angular-scaffolding/pom.xml.

Check out the updated file:

<!-- pom.xml -->
…
<groupId>in.keepgrowing</groupId>
<artifactId>spring-boot-angular-scaffolding</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>pom</packaging>
<name>spring-boot-angular-scaffolding</name>
<description>A multi-module project built with Spring Boot and Angular</description>
<parent>
   …
</parent>
<properties>
   …
</properties>
<modules>
   <module>backend</module>
   <module>frontend</module>
</modules>

If you are working in InteliJ IDE choose Enable Autoimport option when prompted or confirm this option in settings.

Finally, we have a multi-module application. The backend module is the Spring Boot project created with Spring Initializr, what we need now is to set up an Angular project in the frontend module.

The work done in this section is contained in the commit 6963bee6b2aeb0dfa83b4fde28664cfdcd0ab3b0.

Generate an Angular project

Create an Angular project in the frontend/main/src/ folder. We will use the --skip-git option cause we are already tracking the whole project from the main directory, so we don’t need another repository within the project. I will simply name the project angular:

$ cd frontend/src/main
$ ng new --skip-git angular

The angular directory contains the new project:

Angular project structure

The work done in this section is contained in the commit ccc98ee03ef361bce2ccd29501d67cb58c2f4a3b.

Build project with Maven

Configure the frontend-maven-plugin

To build the frontend module with Maven we will use frontend-maven-plugin. It installs node and npm as well as builds our angular project. Be sure that you include the latest tagged version of the plugin (I used 1.6) and add the following code from the <plugins> field to the frontend/pom.xml:

<!-- frontend/pom.xml -->
…
<build>
   <plugins>
      <plugin>
         <groupId>com.github.eirslett</groupId>
         <artifactId>frontend-maven-plugin</artifactId>
         <version>1.6</version>
         <configuration>
            <workingDirectory>src/main/angular</workingDirectory>
            <nodeVersion>v8.11.3</nodeVersion>
            <npmVersion>6.2.0</npmVersion>
         </configuration>
         <executions>
            <execution>
               <id>install node and npm</id>
               <goals>
                  <goal>install-node-and-npm</goal>
               </goals>
            </execution>
            <execution>
               <id>npm install</id>
               <goals>
                  <goal>npm</goal>
               </goals>
            </execution>
            <execution>
               <id>npm run build</id>
               <goals>
                  <goal>npm</goal>
               </goals>
               <configuration>
                  <arguments>run build</arguments>
               </configuration>
            </execution>
         </executions>
      </plugin>
      …
   </plugins>

Take a closer look to the following part of the plugin configuration:

<!-- frontend/pom.xml -->
…
<execution>
   <id>npm run build</id>
   <goals>
      <goal>npm</goal>
   </goals>
   <configuration>
      <arguments>run build</arguments>
   </configuration>
</execution>
…

npm run build calls the npm run command (don’t confuse it with npm build), which will run the angular ng build command to compile an application into an output directory. You can configure the build command in the frontend’s package.json file, so we will add the –prod option to uglify the javascript code:

//frontend/src/main/angular/package.json
{
…
  "scripts": {
    …
    "build": "ng build --prod",
    …
  },

Alter the angular output path

To keep up with the Maven standards we need to alternate the outputPath option for our Angular project, instead of "dist/angular" use "../../../target/frontend":

// frontend/src/main/angular/angular.json
…
"projects": {
    "angular": {
      "architect": {
        "build": {
          …
          "options": {
            "outputPath": "../../../target/frontend",
…

We have just changed the path for the built project, so we need to add the information about the new path to the frontend/pom.xml:

<!-- frontend/pom.xml -->
…
<build>
…
   <resources>
      <resource>
         <directory>target/frontend</directory>
         <targetPath>static</targetPath>
      </resource>
   </resources>
</build>
…

Add the frontend module dependency

Add the frontend dependency to the Spring Boot backend:

<!-- backend/pom.xml -->
…
<dependencies>
…
   <dependency>
      <groupId>in.keepgrowing</groupId>
      <artifactId>frontend</artifactId>
      <version>${project.version}</version>
      <scope>runtime</scope>
   </dependency>
<dependencies>

We used ${project.version} to access the project.version variable and avoid repetition. It’s possible thanks to the fact that “any field of the model that is a single value element can be referenced as a variable”see maven documentation.

Build the project

You can build your project with the Maven plugin directly from the InteliJ IDE – double click clean and then install:

Building Maven project form InteliJ

The successful outcome:

Build output

Alternatively install maven on your machine and execute the build command:

$ sudo apt install maven
$ mvn clean install

Clean the git repository

The frontend-maven-plugin downloads Node and npm from https://nodejs.org/ dist and extracts them into a node folder created in frontend/src/main/angular/ directory. To avoid commiting this folder, add it alongside node_modules to the .gitignore created during generating the Angular project:

# frontend/src/main/angular/.gitignore
# dependencies
/node_modules
/node

The work done in this section is contained in the commit f3f29ede3c5271e455ee3ccfa222563a7e28fc9f.

Prepare the app for development

Fix CORS issues on localhost

To speed up the development process you may want to run the frontend module separately. In that case, the host that serves the frontend (http://localhost:4200/) is different from the host that serves the data (http://localhost:8080/). You are going to encounter the following error:

No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:4200' is therefore not allowed access.

Read the Fix CORS issues between Spring Boot and Angular on localhost post to learn how to enable the cross-domain communication on a development environment.

Allow Angular to handle the routing

All paths implemented in Angular are accessible when the server is started with $ ng serve . However, when you run the whole project built with Maven, the app will give you a Whitelabel Error Page:

There was an unexpected error (type=Not Found, status=404)

The reason is that Spring Boot tries to handle all routes by itself. Check out the Make Spring Boot surrender routing control to Angular post to solve this issue.

Troubleshooting

Check the global settings of maven:

$ mvn --version
Apache Maven 3.5.2
Maven home: /usr/share/maven
Java version: 10.0.1, vendor: Oracle Corporation
Java home: /usr/lib/jvm/java-11-openjdk-amd64

On Ubuntu with JDK older than 8 installed mvn commands will use that version despite the one that is specified in the project properties. You can:

force the usage of JDK 8:

$ export JAVA_HOME=/usr/lib/jvm/java-1.8.0-openjdk-amd64

and check the version now:

$ mvn --version
Apache Maven 3.5.2
Maven home: /usr/share/maven
Java version: 1.8.0_171, vendor: Oracle Corporation
Java home: /usr/lib/jvm/java-8-openjdk-amd64/jre

or you can fix dependencies issues. e.g. data-jpa requires jaxb-api in JDK9, so add it to the backend pom file:

<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
   <groupId>javax.xml.bind</groupId>
   <artifactId>jaxb-api</artifactId>
   <version>2.3.0</version>
</dependency>

The problem doesn’t occur when you build the project directly from InteliJ.

Photo by Vero Photoart on StockSnap

little_pinecone

View Comments

  • Hello. Nice tutorial. One question: If i run my fat jar on a pc in a local area network, if i access on another pc the ip of the first pc using port 8080 my app renders. But, if i add controllers to my backend, a simple get requests gets "connection refused" on the second pc(the pc that doesn't run the fatjar).

    Do you have a solution to this?

    Kind regards,

  • Excellent article. I'm having difficulties running it from the command line:

     

    $ ./mvnw spring-boot:run

    Yields:

    [ERROR] Failed to execute goal org.springframework.boot:spring-boot-maven-plugin:2.2.5.RELEASE:run (default-cli) on project spring-boot-angular-scaffolding: Unable to find a suitable main class, please add a 'mainClass' property -> [Help 1]

    • Thank you for bringing this issue to my attention!

      To run this multi-module project from the command line I needed to change the `spring-boot-maven-plugin` configuration in the `parent` and `backend` `pom.xml` files according to the solution given in this StackOverflow issue:

      
      
      …
      
          
              
                  org.springframework.boot
                  spring-boot-maven-plugin
                  
                      true
                  
              
          
       
      …
      
      
      
      …
      
          
              
                  org.springframework.boot
                  spring-boot-maven-plugin
                  
                      false
                  
              
          
       
      …
      

      I fixed the issue in the 2.0.1 release. However, if you don't wan't to update your project, just edit both `pom` files manually.

Recent Posts

Simplify the management of user roles in Spring Boot

Spring Security allows us to use role-based control to restrict access to API resources. However,…

3 years ago

Create a custom annotation to configure Spring Boot tests

A custom annotation in Spring Boot tests is an easy and flexible way to provide…

3 years ago

Keycloak with Spring Boot #4 – Simple guide for roles and authorities

Delegating user management to Keycloak allows us to better focus on meeting the business needs…

3 years ago

Keycloak with Spring Boot #3 – How to authorize requests in Swagger UI

Swagger offers various methods to authorize requests to our Keycloak secured API. I'll show you…

3 years ago

Keycloak with Spring Boot #2 – Spring Security instead of Keycloak in tests

Configuring our Spring Boot API to use Keycloak as an authentication and authorization server can…

3 years ago

Keycloak with Spring Boot #1 – Configure Spring Security with Keycloak

Keycloak provides simple integration with Spring applications. As a result, we can easily configure our…

3 years ago