A Minimalist Guide to Building Spring Boot Angular 5 Applications


Over the last year, I have worked with many teams using Spring Boot with Angular/React for developing Java web applications. I consider Spring Boot a pragmatic opinionated way to build Spring applications. Spring Boot makes it easy to build web applications in Java and provides a productive environment for development.

One thing that I observed is that many teams have stopped applying good practices while building Spring Boot applications. The practices that I am talking here include modularising codebase, enabling static code analysis tools for code quality, release management, different modules for frontend and backend code, etc. Everyone wants to use Microservice architecture when they don’t even understand how to build modular applications. Yesterday, I was talking to a startup that has yet to write single line of business code but has wasted last many days finalising their cool technical stack(Next.js, GraphQL, React, Relay, Node, etc.). I don’t know where we are going with all this BS but that is a rant for some other day.

In this post, I will walk you through setting up Spring Boot Angular 5 project for building production grade applications. We will make use of Maven build tool. We will create separate modules for frontend and backend so that frontend and development teams can easily work on the project. In many projects, I saw people keeping frontend assets in src/main/resources. It then becomes difficult to use build tool and package manager for frontend code.

Prerequisite

To follow along you will need following on your machine.

  1. Java 8: Download and install the JDK from official website.
  2. Node.js : Download and install the latest version of node.
  3. Angular CLI: It is the official way to bootstrap Angular applications. You can install it using your favourite package manager. For npm, you can type npm install -g @angular/cli on your command-line. If you use yarn, you can type yarn global add @angular/cli on your command-line.

Now that we have installed all the prerequisites let’s get started.

Create a multi-module Spring Boot Angular 4 application

In the remaining part of this post, we will create a multi-module Spring Boot application. There will be two modules:

  1. backend: This contains Spring Boot based backend written in Java.
  2. frontend: This contains Angular 5 based frontend of the application.

By the end of this post, you will be able to build the whole application as a single fat jar using Maven.

Step 1: Create Spring Boot project using start.spring.io

The easiest way to create a Spring Boot project is to use Spring Initializr project available at http://start.spring.io .You can use either the web user interface or tool like cURL to create the project. Below is the cURL command that will create the starter project in the myapp directory.

$ curl https://start.spring.io/starter.zip \
-d dependencies=web,data-jpa,devtools,h2 \
-d groupId=com.shekhargulati -d artifactId=myapp -d name=myapp \
-d description="Spring Boot Angular 5 Application" -d baseDir=myapp \
-o myapp.zip && unzip myapp.zip && rm -f myapp.zip

Please note that you don’t have to type $. $ signifies shell prompt.

You need to have unzip utility installed on your machine. If you don’t have required tools, then you can create app using http://start.spring.io/ web interface.

The above will create Apache Maven based Spring Boot project. The project uses maven-wrapper that provides an easy way to ensure a user of your Maven build has everything necessary to run your Maven build. We have specified following dependencies:

  1. web: As this will be a web application so we have specified web as our dependency. This will add spring-boot-starter-web dependency to project’s build file pom.xml. The
  2. data-jpa: The application will store data in PostgreSQL. The data-jpa will add spring-boot-starter-data-jpa to the pom.xml file. Spring Data JPA makes it easy to work with relational database.
  3. devtools: This adds hot reloading to the application. This makes development more productive.
  4. h2: This adds in-memory database h2 dependency to the pom.xml. This is essential for writing integration tests.

To build the project you can run the following command.

$ cd myapp
$ ./mvnw clean install

If you are Windows user, then you can the following command.

$ mvn.bat clean install

The above command will first download the required Maven version and then build the whole project.

To run the project, you can use ./mvnw spring-boot:run command.

Step 2: Convert to a multi-module Maven project

The project generated by Spring Initializr has a single module. As mentioned above, we need a multi-module project so we will update our project structure to make it a multi-module project with two modules — backend and frontend.

Change directory to myapp and create the following directory structure. On *nix systems, you can run mkdir backend frontend.

├──.mvn
    └── wrapper
      ├── maven-wrapper.jar
      └── maven-wrapper.properties
├── backend
├── frontend
├── mvnw
├── mvnw.cmd
├── pom.xml
├── src
│   ├── main
│   └── test

Update the pom.xml by adding frontend and backend project to the modules section as shown below.

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.8.RELEASE</version>
        <relativePath/>
    </parent>

    <groupId>com.shekhargulati</groupId>
    <artifactId>myapp</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>pom</packaging>
    <modules>
        <module>frontend</module>
        <module>backend</module>
    </modules>

    <name>myapp</name>
    <description>Spring Boot and Angular application</description>

</project>

In the above shown pom.xml, we also updated packaging to pom. pom acts as a container of submodules, each submodule is represented by a subdirectory in the same directory as pom.xml with pom packaging.

Move the src directory to the backend directory and create a pom.xml with the content as shown below.

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>com.shekhargulati</groupId>
        <artifactId>myapp</artifactId>
        <version>0.0.1-SNAPSHOT</version>
    </parent>

    <artifactId>myapp-backend</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-websocket</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>com.h2database</groupId>
            <artifactId>h2</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.postgresql</groupId>
            <artifactId>postgresql</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

We added all project dependencies to thebackend pom.xml as this module will house our backend code. This is identical to the pom.xml created with Spring Initialzr expect that we have to add parent section. The parent pom for myapp-backend module is identified by groupId com.shekhargulati and artifactId myapp.

Now, we will update the frontend module. Create a new pom.xml inside the frontend directory and populate it with following contents.

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>com.shekhargulati</groupId>
        <artifactId>myapp</artifactId>
        <version>0.0.1-SNAPSHOT</version>
    </parent>

    <artifactId>myapp-frontend</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>

</project>

After making the changes, run the ./mvnw clean install from the project root. The whole build should run fine and you should see output as shown below.

[INFO] ------------------------------------------------------------------------
[INFO] Reactor Summary:
[INFO]
[INFO] myapp .............................................. SUCCESS [  0.228 s]
[INFO] myapp-frontend ..................................... SUCCESS [  0.508 s]
[INFO] myapp-backend ...................................... SUCCESS [  4.735 s]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 5.739 s
[INFO] Finished at: 2017-11-08T16:59:25+05:30
[INFO] Final Memory: 36M/324M
[INFO] ------------------------------------------------------------------------

Step 3: Create Angular 5 project

In the previous step, we created the multi-module Maven Spring Boot application but we haven’t yet created the Angular project inside the frontend module.

Go inside the frontend directory and create a new Angular project using the Angular CLI.

$ ng new myapp --directory .

This will generate the sample project in the frontend directory and install all the dependencies using npm package manager. You can run the application using ng serve command.

$ ng serve -o

The above command will start the application locally at http://localhost:4200/. You can also run the application using npm or yarn. Npm users can use npm start . Yarn users can use yarn start to start the application. You can learn more about the default project generated by Angular CLI by reading the documentation.

Step 4: Build frontend code using Maven build tool

In the previous step, we created the Angular project using the Angular CLI. The project can be built and run using the package manager of your choice or using the ng command-line. In development mode, you would use those methods, but during deployment both should be packaged as a single artifact. This means we should be using a single tool to build the application. The single tool in our case is Apache Maven. In this step, we will make both frontend and backend modules bundle as a single JAR.

To run the yarn commands using the Maven build tool, we will use frontend-maven-plugin. This plugin comes with a set of built-in goals that we can use for triggering different yarn commands. Replace the content of frontend/pom.xml with the content shown below.

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <groupId>com.shekhargulati</groupId>
        <artifactId>myapp</artifactId>
        <version>0.0.1-SNAPSHOT</version>
    </parent>
  <modelVersion>4.0.0</modelVersion>

  <artifactId>myapp-frontend</artifactId>

  <build>
    <resources>
      <resource>
        <directory>dist</directory>
      </resource>
    </resources>
    <plugins>
      <plugin>
        <groupId>com.github.eirslett</groupId>
        <artifactId>frontend-maven-plugin</artifactId>
        <version>1.6</version>
        <configuration>
          <nodeVersion>v8.9.0</nodeVersion>
          <yarnVersion>v1.3.2</yarnVersion>
        </configuration>
        <executions>
          <execution>
            <id>install node and yarn</id>
            <goals>
              <goal>install-node-and-yarn</goal>
            </goals>
            <phase>generate-resources</phase>
          </execution>
          <execution>
            <id>yarn install</id>
            <goals>
              <goal>yarn</goal>
            </goals>
            <configuration>
              <arguments>install</arguments>
            </configuration>
          </execution>
          <execution>
            <id>yarn lint</id>
            <goals>
              <goal>yarn</goal>
            </goals>
            <configuration>
              <arguments>lint</arguments>
            </configuration>
          </execution>
          <execution>
            <id>yarn build</id>
            <goals>
              <goal>yarn</goal>
            </goals>
            <phase>generate-resources</phase>
            <configuration>
              <arguments>build</arguments>
            </configuration>
          </execution>
          <execution>
            <id>yarn test</id>
            <goals>
              <goal>yarn</goal>
            </goals>
            <phase>test</phase>
            <configuration>
              <arguments>test --watch false</arguments>
              <failOnError>true</failOnError>
            </configuration>
          </execution>
        </executions>
      </plugin>
    </plugins>
  </build>
</project>

This plugin will first download and install node and yarn on your machine and then it will invoke different goals yarn install, yarn lint, and yarn build during the Maven build lifecycle.

You can run the build of the frontend code using the ../mvnw clean install command. It will invoke yarn commands to build the project. The build output will be placed inside the dist directory of frontend module.

One interesting thing to note here is that we are packaging application as a WebJar. WebJar allows you to package your client side dependencies as a JAR file. Spring Boot is preconfigured to handle them. It will serve the index.html in the jar’s META-INF/resources directory. I find this a clean way to package frontend code.

One last change that you have to make is change the build output directory. This is required because WebJar package static content in the META-INF/resources directory. Update your package.json build script with the one show below.

$ ng build -prod --output-path dist/META-INF/resources

Update the backend pom.xml to include dependency on frontend. This is required to make sure frontend jar is included in the final Spring Boot executable. Thanks Stuart for pointing this out. You can refer to the backend pom.xml in the project’s Github repository.

<dependency>
    <groupId>com.shekhargulati.starters.ngboot</groupId>
    <artifactId>frontend</artifactId>
    <version>${project.parent.version}</version>
</dependency>

Now, build the full application by executing ./mvnw clean install command inside the myapp directory. This will build the Java executable Jar in the backend/target directory. You can run the jar as shown below.

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

You can view the application by firing up your favourite browser http://localhost:8080. You will see Angular application rendered as shown below.

Spring Boot Angular 5 Starter

To make it easy for you, I have created a Spring Boot Angular 5 starter project that add more goodies to the stuff covered in this blog. You can give shekhargulati/spring-boot-maven-angular-starter a try. There are many useful features in the starter as mentioned below:

  1. Multi module Maven project: A multi module project to modularize backend and frontend code separately.
  2. Maven wrapper: So, you don’t need to install Maven on your machine.
  3. Checkstyle: Enforce sane coding standard guidelines.
  4. ErrorProne: Find errors in your code.
  5. Frontend packaged as a WebJar.
  6. CORS enabled: A global configuration is added to enable CORS so that frontend can work seamlessly with backend during development.
  7. REST API base path: Sets the base REST API path to /api. You can configure it by changing rest.api.base.path property.
  8. Maven release plugin
  9. CI: The project is preconfigured to use TravisCI as continuous integration server.

Conclusion

In this post, you learnt how to create an Apache Maven multi-module Spring Boot application. Your learnt how to create Angular 5 application and bundle it as a single application using the frontend-maven-plugin. Now, you have the strong base on which you can start building your application.

48 thoughts on “A Minimalist Guide to Building Spring Boot Angular 5 Applications”

  1. Very good! What about development? Is it possible start back and front end configuring all content in only one java/maven project so that it have hotdeploy as if it had been deployed individually?

      1. Did you try to run as one entity during the development stage?
        No,
        You can run it as MVC (any way you don’t use boot at the production) with intelliJ
        plus write a proxy.ts to run angular with port 8080 and make development at the same time.
        one more… you must run a special plugin to be able to edit a static content.
        Let me know when you succeed….

  2. Congrats, great article!

    I’ve been thinking what is the best way to handle deploy production in this cenario. Here are some thoughts I had and I’d love to hear your opnion.

    1. Using your approach I’ll be deploying client and server as one application. Is there any down side for this? I ask cause I read some people stating that we should use separated deploys for frontend and backend.

    2.What about URL bookmarking? Using this approach won’t Spring mess thing up? I mean, all requests will be handled by Spring MVC instead of Angular.

    Thanks in advance.

    1. Thanks Viny for your comment. Below are my views on the questions you asked

      1. I don’t think there is any downside in deploying frontend and backend together. It all depends on the use-case. Many teams use Spring Boot for building monolithic apps and for them multi modular project is a better fit than keeping frontend code in src/main/resources. The post suggests that you should start by putting frontend and backend as separate modules. In future when you have a need for separate deployment then you can easily do that as well. It gives separation of concerns and helps team work better.

      2. If you use push state URL in Angular then yes it will be an issue. One solution that I have used is to create a catch all route in the backend that returns the index.html as mentioned in this stackoverflow post https://stackoverflow.com/questions/38516667/springboot-angular2-how-to-handle-html5-urls

  3. BIG problem with this article is it doesn’t state that you need to add the frontend project as a dependency of the backend project. Without this you will be up until 2AM figuring out why the frontend jar isn’t included in the backend build. Obvious I know once you realize it but I thought there was some special setting somewhere that did this outside of Maven.

    Check the pom.xml of the backend module in the Git repository with the source of this article. I believe it is the first dependency.

    1. Thanks Stuart for pointing this out. I have update the post to include this point. Also, I always add a link to the Github repository in my blogs so that people can look at the actual source code in case I miss something.

  4. Thanks shekhargulati!
    I followed exactly your guide, but for localhost:8080 I get a “Whitelabel Error Page”.
    Just the Frontend, with localhost:4200, all is fine.
    Any Idea?

  5. I’m trying to follow your instructions, but when I move the src to the backend folder, its Test fails, gives me an Failed to load ApplicationContext exception. I’m uncertain why I’m running into this error

  6. Thank you. This helped me a lot.

    Maybe you can change one thing in “build”: “ng build -prod –output-path dist/META-INF/resources”
    –output-path does not actually start with a dash (-) but with this other character “–” which is wrong and will not work.

  7. Hi,
    Thanks a lot for this very very helpful article.
    Could you add a “Hello World” RestController and consume it from the frontend app, then we can see how to continue the developpement.
    I am starting learning Spring Boot + Angular but with this way to configure I don’t see how to continue developpement.
    I took a look in your git repository, why did you add “Thymeleaf” as a dependency since the project is full REST?
    Thanks again

  8. Thanks for the writeup, it helped me a lot!
    There is one question though. How do you work in dev mode in this setup? I mean, using this setup you have two different servers one for angular (ng serve) and one for spring/java (tomcat or so). Both run on different ports – so you need to have some logic to reconfigure your client app to work with a different base URL.
    You setup CORS in your repo but is there also some examples on how to implement this behavior for dev the best way?
    Cheers!

    1. Hello Basti, the way I work is by making use of Angular environment. You should inject your base api url based on the environment. I will update the blog with the real example over the weekend.

    1. Hello Vikram, I don’t see why it should be a problem. You have to change the packaging to WAR as recommended by Spring Boot and then give it a try. Are you getting any error doing that?

  9. Hi Shekhar,
    I have a existing project of angular 4.3 using npm build.
    I want to build my front-end using npm instead of yarn. Would you please assist me what i need to change and add any extra mvn dependency.

  10. Thanks for this very usefull guide.

    What do you think about Security Integration (user Authentication) in the backend and the front end of your code base?

  11. Trying to troubleshoot a project I setup mainly following this guide.

    I’m currently getting a “Whitelabel error page” which I know comes from Spring MVC when I try to refresh an Angular page.

    Looking at the sample code from this post in the Github repository, I was wondering what the ‘webMvcRegistrationsHandlerMapping’ method in the Application class of Spring Boot did. This looks to be manually registering the paths of the backend API services but I understood Spring Boot would do this automatically anyway.

    I’m just wondering if this method is there to somehow reduce the number of URLs that Spring Boot tries to intercept and let Angular handle the rest?

      1. What I had to do was factor out the configuration into a separate @Configuration class and add an error page for the ‘NOT_FOUND’ status. Then you add a ViewController to forward this ‘/notFound’ error page back to the root of the application. This should get back into Angular and then let Angular interpret the URL in the browser and find what should be shown.

        The code below also shows the Cors mapping that is needed so the front-end running in development mode on a different port is allowed to talk to the back-end API services. The Cors mapping was already in the sample code from the article but I don’t remember if it was split into a separate class or not.

        import org.springframework.boot.context.embedded.EmbeddedServletContainerCustomizer;
        import org.springframework.boot.web.servlet.ErrorPage;
        import org.springframework.context.annotation.Bean;
        import org.springframework.context.annotation.Configuration;
        import org.springframework.http.HttpStatus;
        import org.springframework.web.servlet.config.annotation.CorsRegistry;
        import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
        import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;

        @Configuration
        public class WebApplicationConfig extends WebMvcConfigurerAdapter {
        @Override
        public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping(“/**”).allowedMethods(“*”).allowedOrigins(“http://localhost:4200”);
        }

        @Override
        public void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController(“/notFound”).setViewName(“forward:/index.html”);
        }

        @Bean
        public EmbeddedServletContainerCustomizer containerCustomizer() {
        return container -> {
        container.addErrorPages(new ErrorPage(HttpStatus.NOT_FOUND,
        “/notFound”));
        };
        }
        }

      2. Thank you so much Stuart for your comment! I have already solved it, I had some errors in pom files.

  12. Compliments…very useful guide
    Only one thing; in the package.json of the angular frontend i had to enter this string for the build parameter:
    “build”: “ng build –prod –output-path ui-src/META-INF/resources”

    For the ‘prod’ argument there are two ‘-‘ rather than only one

    Hope this can be helpful

  13. Very good guide and I do appreciate it!

    One thing is “generate-resources” is needed to get your frontend changes updated every time you run “mvn clean install”.

    I use npm instead of yarn with frontend-maven-plugin and forgot to put “generate-resources” in. Turns out my frontend changes doesn’t get updated anymore until I put it back in.

    1. I have put generate-resource in my pom.xml but still the front-end updated code is not reflecting .
      Could you please help me to find out where did I make mistake.
      po.xml for front-end application

      4.0.0
      my-admin-web

      com.klm.passenger.my.admin
      my-admin
      1.0.0-RC01

      dist

      com.github.eirslett
      frontend-maven-plugin
      1.6

      v8.9.0
      v1.3.2

      install node and yarn

      install-node-and-yarn

      generate-resources

      yarn install

      yarn

      install

      yarn build

      yarn

      generate-resources

      build

  14. Hey Nice artical this works, But I have a question here

    I want to controller which maps to root i.e., @RequestMapping(value=””,method = RequestMethod.GET)
    In this I need to check a condition and when tru forward to some compponent. Please explain how to do this.

    Trying to Do:
    @RequestMapping(value=”/sq”,method = RequestMethod.GET)
    public String redirect(HttpServletRequest request){
    if(request.something = “something”){
    return “index.html “; //or any other page
    }else{
    return “error-page”;
    }

  15. Spring Boot and Spring MVC are not comparable or mutually exclusive. If you want to do web application development using Spring, you would use Spring MVC anyway. Thanks for sharing this.

  16. We are in a closed network so we are not using frontend plugin
    Can you show how to install and build with exec-maven-plugin

  17. You sir are a genius. Excellent article and very well articulated.
    I’m a big fan of you and waiting for your next blog on block chain.

  18. Hi,

    I have agradle based project. So I added compile group: ‘com.github.eirslett’, name: ‘frontend-maven-plugin’, version: ‘1.6’ in build.gradle. Now as you said this will take care of installs for npm, node but does it build frontend components? I ran ng build after adding that dependency and it generated files in resources/static directory(updated outputPath: /resources/static/ in angular.json) . But starting tomcat server I don’t see them served at 8080. Could you please point out if I’m missing something or is this to be handled differently for gradle based projects?

  19. Nice! You can also use the following if you don’t want to change the output dir of the angular build:

    dist
    META-INF/resources
    false

    P.S adding the ui module to the web module dependencies has me struggling for so long! Thanks for pointing that out.

    1. @cmptrwizard thanks for that information, I was looking at ways to specify output dir outside of package.json (by not setting the –output-path ) Is there a way from frontend-maven-plugin or otherwise in maven where i could set this?

  20. Hi. Nice article, but I have few questions.
    First, why when we are accessing to localhost:8080 the index.html is returned?
    Second, how to consume some rest enpoint from angular?

    Regards

Leave a Reply to Ranveer Cancel reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: