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
Please enter at least three characters to search
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

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
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

The software you build is only as secure as the code that powers it. Learn how malicious code creeps into your software supply chain.

Apache Cassandra combines the benefits of major NoSQL databases to support data management needs not covered by traditional RDBMS vendors.

Generative AI has transformed nearly every industry. How can you leverage GenAI to improve your productivity and efficiency?

Modernize your data layer. Learn how to design cloud-native database architectures to meet the evolving demands of AI and GenAI workloads.

Related

  • Achieving Micro-frontend Architecture Using Angular Elements
  • Auto-Scaling a Spring Boot Native App With Nomad
  • How to Use Shipa to Make Kubernetes Adoption Easier for Developers
  • Lessons Learned Moving From On-Prem to Cloud Native

Trending

  • Automating Data Pipelines: Generating PySpark and SQL Jobs With LLMs in Cloudera
  • 5 Subtle Indicators Your Development Environment Is Under Siege
  • Understanding IEEE 802.11(Wi-Fi) Encryption and Authentication: Write Your Own Custom Packet Sniffer
  • Designing a Java Connector for Software Integrations
  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.

By 
Vasyl Kutsyk user avatar
Vasyl Kutsyk
·
Updated Dec. 17, 19 · Tutorial
Likes (1)
Comment
Save
Tweet
Share
14.4K 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.

Related

  • Achieving Micro-frontend Architecture Using Angular Elements
  • Auto-Scaling a Spring Boot Native App With Nomad
  • How to Use Shipa to Make Kubernetes Adoption Easier for Developers
  • Lessons Learned Moving From On-Prem to Cloud Native

Partner Resources

×

Comments
Oops! Something Went Wrong

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

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

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends:

Likes
There are no likes...yet! 👀
Be the first to like this post!
It looks like you're not logged in.
Sign in to see who liked this post!