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.
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.
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. |
spring-boot-angular-scaffolding
.$ ng --version Angular CLI: 6.0.8 Node: 8.11.3 OS: linux x64 Angular: 6.1.1
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:
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:
spring-boot-angular-scaffolding
;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.
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.
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.
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:
The work done in this section is contained in the commit ccc98ee03ef361bce2ccd29501d67cb58c2f4a3b.
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", … },
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 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.
You can build your project with the Maven plugin directly from the InteliJ IDE – double click clean
and then install
:
The successful outcome:
Alternatively install maven on your machine and execute the build command:
$ sudo apt install maven $ mvn clean install
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.
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.
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.
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
Spring Security allows us to use role-based control to restrict access to API resources. However,…
A custom annotation in Spring Boot tests is an easy and flexible way to provide…
Delegating user management to Keycloak allows us to better focus on meeting the business needs…
Swagger offers various methods to authorize requests to our Keycloak secured API. I'll show you…
Configuring our Spring Boot API to use Keycloak as an authentication and authorization server can…
Keycloak provides simple integration with Spring applications. As a result, we can easily configure our…
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,
Hi,
I haven't run this project in a local network, but maybe I can still be of some use :)
Maybe you can't get a response from a different machine in the network because of the Cross Origin Resource Sharing issue?
When I run the frontend separately with $ ng serve (so it was running on http://localhost:4200), the API (http://localhost:8080/api) refused to allow requests from the other host.
I allowed other origins to access the API (Fix CORS issues between Spring Boot and Angular on localhost) on the development profile.
Btw, I also had to make Spring Boot to surrender control over routing to Angular (Make Spring Boot surrender routing control to Angular).
I'll try to run my project in a local network to investigate the issue if I have the time.
I hope you will fix your problem in no time :)
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]
Are you sure you are running it from the backend's root directory?
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:
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.