DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports Events Over 2 million developers have joined DZone. Join Today! Thanks for visiting DZone today,
Edit Profile Manage Email Subscriptions Moderation Admin Console How to Post to DZone Article Submission Guidelines
View Profile
Sign Out
Refcards
Trend Reports
Events
Zones
Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones AWS Cloud
by AWS Developer Relations
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones
AWS Cloud
by AWS Developer Relations
Building Scalable Real-Time Apps with AstraDB and Vaadin
Register Now

Trending

  • What Is mTLS? How To Implement It With Istio
  • Why You Should Consider Using React Router V6: An Overview of Changes
  • Chaining API Requests With API Gateway
  • MLOps: Definition, Importance, and Implementation

Trending

  • What Is mTLS? How To Implement It With Istio
  • Why You Should Consider Using React Router V6: An Overview of Changes
  • Chaining API Requests With API Gateway
  • MLOps: Definition, Importance, and Implementation
  1. DZone
  2. Software Design and Architecture
  3. Cloud Architecture
  4. Multi-Environment Angular With Dynamic Backend URI in Docker Compose

Multi-Environment Angular With Dynamic Backend URI in Docker Compose

This article goes through the construction of an Angular web application with multiple environments and demonstrates deployment with Docker Compose.

Vasyl Kutsyk user avatar by
Vasyl Kutsyk
·
Updated Dec. 17, 19 · Tutorial
Like (1)
Save
Tweet
Share
12.78K Views

Join the DZone community and get the full member experience.

Join For Free

Multi-environmental Angular application with docker-compose

Multi-environmental Angular application with docker-compose


Our goals are simple — one frontend for different backends. Build once, reuse everywhere.

We did it with Angular 8 and docker-compose, and we are deploying our Angular application without rebuilding it for different backends.

Every client will own his own copy of the solution, without a centralized backend service. So our frontend should be able to change his requests endpoint dynamically during the start process.

Our delivery process is:

  • Start installation script
  • Start application
  • Enjoy working application

As we don’t develop a centralized backend, we need our front-end application to be customizable for backend URI. And an important point here is that we decided to develop our front application with Angular 8. This means that after build we will receive static files **html/css/js**, which should include backend URI.

If you worked with Angular applications you know that it may not work out-of-the-box, so we will add our own service, which will manage backend URI.

Standard Web App workflow

WebApp workflow

WebApp workflow


Web App Workflow

0: A user connects to a browser

1: The user inputs URL and the browser asks Server for files;

2: The user browser receives static files html/css/js which are the Angular web app.

3: Those steps represent user interaction with web app, which will communicate with the backend server and build representation on the client-side with JS code.

So by default, when we deploy our web app to the server it has to have backend service URI hardcoded in files, which is done automatically with Angular CLI during the build process.

You can find more about Angular in the official documentation.

Multi Backend Angular Web App

Our implementation differs from the original, with an additional file config.json which is in the assets directory and is served with other Angular files.

Content of the file:

Java




x


 
1
// config.json example
2
{
3
    "url": "https://my_backend.uri"
4
}


 Here is our requests stack trace for the Angular web app:

Stacktrace of WebApp

From step 1-8, we are downloading files needed for Angular. And on step 9 we are receiving our config.json, which contains the URL of our backend service, to which the web app will send all requests.

Stacktrace of WebApp


Implementation Angular

I have created file config.teml.json which contains next template,

JSON




x


 
1
{
2
    "url": "https://${SERVER_NAME}:${PORT_BACK_SSL}" 
3
}


where SERVER_NAME is the backend service IP or domain name, and PORT_BACK_SSL is the backend service port for SSL communication if needed.

 Then we should create a service which will configure our variables and inject them into the Angular app:

Java




x
30


 
1
// app-config.ts
2
import { Injectable } from '@angular/core';
3
import { HttpClient } from '@angular/common/http';
4
interface Config {
5
  url: string;
6
}
7
export interface IAppConfig {
8
  baseUrl: string;
9
  baseDMUrl: string;
10
  baseStandardUrl: string;
11
  load: () => Promise<void>;
12
}
13
@Injectable()
14
export class AppConfig implements IAppConfig {
15
  public baseUrl: string;
16
  public baseDMUrl: string;
17
  public baseStandardUrl: string;
18
constructor(private readonly http: HttpClient) {}
19
public load(): Promise<void> {
20
    return this.http
21
      .get<Config>('assets/config.json')
22
      .toPromise()
23
      .then(config => {
24
        this.baseUrl = config.url;
25
      });
26
  }
27
}
28
export function initConfig(config: AppConfig): () => Promise<void> {
29
  return () => config.load();
30
}


   

After, of course, we should inject this service into our app.module.ts:

Java




xxxxxxxxxx
1
27


 
1
import { AppConfig, initConfig } from './app-config';
2
import { NgModule, Inject, APP_INITIALIZER } from '@angular/core';
3
import { HttpClientModule } from '@angular/common/http';
4
import { BrowserModule } from '@angular/platform-browser';
5
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
6
import { AppComponent } from './app.component';
7
@NgModule({
8
  imports: [
9
    BrowserModule,
10
    BrowserAnimationsModule,
11
    HttpClientModule,
12
  ],
13
  providers: [
14
    AppConfig,
15
    {
16
      provide: APP_INITIALIZER,
17
      useFactory: initConfig,
18
      deps: [AppConfig],
19
      multi: true
20
    },
21
  ],
22
  declarations: [AppComponent],
23
 
24
  bootstrap: [AppComponent]
25
})
26
export class AppModule {
27
}


As you can see we are using the APP_INITIALIZER  provider to initialize our AppConfig module.

Docker

The Docker image for our Angular application is based on Nginx:

Java
 




x


 
1
# base image
2
FROM nginx:stable-alpine
3
# clean NGINX static files
4
RUN rm -rf /usr/share/nginx/html/*
5
# copy WebApp built files into NGINX directory
6
COPY ./dist/app /usr/share/nginx/html


Docker Compose

Our Docker Compose file is key for multi-backend deployment with an Angular app:

Java




x
17


 
1
version: "3"
2
services:
3
  backend:
4
    image: privateregistry.azurecr.io/cc/backend:develop
5
    expose:
6
      - "8080"
7
frontend:
8
    image: privateregistry.azurecr.io/cc/frontend:develop
9
    ports:
10
      - "80:80"
11
      - "443:443"
12
      - "8080:8080"
13
    links:
14
      - backend:backend
15
    env_file:
16
      - ./backend.env
17
    command: /bin/sh -c "envsubst '$${SERVER_NAME},$${PORT_BACK_SSL}' < /usr/share/nginx/html/assets/config.templ.json > /usr/share/nginx/html/assets/config.json && exec nginx -g 'daemon off;'"


And the key component here is the last line:

Shell




x


 
1
command: /bin/sh -c "envsubst '$${SERVER_NAME},$${PORT_BACK_SSL}' < /usr/share/nginx/html/assets/config.templ.json > /usr/share/nginx/html/assets/config.json && exec nginx -g 'daemon off;'"


   

Where we execute swapping of strings  ${SERVER_NAME}  and  ${PORT_BACK_SSL}  in config.templ.json and we store this file in place of  config.json which will be used by the frontend.

 Values of those variables are taken from the environment variables for the Docker Compose environment, which are initialized in the file backend.env    

Java




x


 
1
SERVER_NAME=mydomainfortest.westeurope.cloudapp.azure.com
2
PORT_BACK_SSL=443


Automation

This moment is essential because creating an undefined number of files that contain separate  SERVER_NAME instances for each client will be overcomplicated.

So I use this script, which is executed before each pull of images:

Java
 




x


 
1
export SERVER_NAME=$(curl https://ipinfo.io/ip) && \
2
echo -e "\nSERVER_NAME="$SERVER_NAME >> backend.env


Thanks for reading!

If you have other errors during deployment or you interested in another topic, please add comments. I'm interested in the dialog.

Further Reading

Deployment Automation for Multi-Platform Environments

Dockerizing an Angular App via Nginx [Snippet]
Uniform Resource Identifier AngularJS Docker (software) app Web Service application

Published at DZone with permission of Vasyl Kutsyk. See the original article here.

Opinions expressed by DZone contributors are their own.

Trending

  • What Is mTLS? How To Implement It With Istio
  • Why You Should Consider Using React Router V6: An Overview of Changes
  • Chaining API Requests With API Gateway
  • MLOps: Definition, Importance, and Implementation

Comments

Partner Resources

X

ABOUT US

  • About DZone
  • Send feedback
  • Careers
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 600 Park Offices Drive
  • Suite 300
  • Durham, NC 27709
  • support@dzone.com

Let's be friends: