How to make SpringBoot + AngularJS applications run on a single port?

How to make SpringBoot + AngularJS applications run on a single port?

It is a common practice to use Springboot for developing backend REST APIs and JS frameworks like Angular, React, etc are used for developing front-end applications. These are essentially two different applications that talk to each other using REST APIs.

A SpringBoot application by default runs on port 8080
An AngularJS application by default runs on 4200.

It's very flexible for a backend developer to not worry about what's going with front end application as long as a mutual API contract is figured out. Everything runs smoothly if both the developers stick to their agreed-upon API contracts.

Let's consider the below situation of a library application where both are hosted separately.

Backend REST APIs URL:

http://myapplication:8080/api/books
Run server: mvn spring-boot:run

Frontend library application URL:

http://myapplication:4200/books
Run server: ng serve

When you do this, you won't be able to use relative paths to the backend APIs also you may have to enable CORS policy as the requests are coming from a different domain. Also, if the application is relatively small, it is easier to deploy it as a single application.

Now, to achieve running both applications on same node, all you have to is to add a WebConfig file that routes any requests that aren't listed in Spring controllers route to index.html of frontend which well then take care of routing. In simple terms, you are basically making Springboot say, "Hey Angular! I am not aware of this route. Please, take care of this". Now, similar to Spring Controller classes, Angular has routing too which will route the request accordingly!

@Configuration
public class WebConfig implements WebMvcConfigurer {
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/**")
                .addResourceLocations("classpath:/public/")
                .resourceChain(true)
                .addResolver(new PathResourceResolver() {
                    @Override
                    protected org.springframework.core.io.Resource getResource(String resourcePath, org.springframework.core.io.Resource location) throws IOException {
                        Resource requestedResource = location.createRelative(resourcePath);

                        return requestedResource.exists() && requestedResource.isReadable() ? requestedResource
                                : new ClassPathResource("/public/index.html");
                    }
                });
    }
}

Also, make sure your AngularJS build artifacts are created at resources folder of SpringBoot by setting the outputDir path in angular.json as shown below

"build": {
          "builder": "@angular-devkit/build-angular:browser",
          "options": {
           ** "outputPath": "../resources/public/",**
            "index": "src/index.html",
            "main": "src/main.ts",
            "polyfills": "src/polyfills.ts",
            "tsConfig": "tsconfig.app.json",
            "aot": true,
            "assets": [
              "src/favicon.ico",
              "src/assets"
            ],
            "styles": [
              "src/styles.css"
            ],
            "scripts": []
          }

Next step would be to build your AngularJS application as part of mvn so that you can get both applications as a jar file.

Now, when you run,

mvn spring-boot:run

You can access both front end and back end at

http://myapplication:8080/api/books
http://myapplication:8080/books

Hope this helps. Follow me on twitter for more updates.