user5656370
user5656370

Reputation:

Configuration of Angular2 application - nginx and docker

I have a REST back-end service located on some server and front-end application made in angular. I'm using Angular CLI to build application. Location of my back-end server is located in environment file. Requirement for my app is that I'm providing two docker images. One with my back-end server (Java Spring Boot app) and the second one is static html build with ng build myApp command. Then i copy content od dist directory to proper directory on docker image as is shown here Nginx docker image.

Problem is, that back-end and front-end may work on different servers. Is there any way i can configure my front-end app that i can change back-end server location as per start of container?

Upvotes: 4

Views: 3700

Answers (1)

Marc Demierre
Marc Demierre

Reputation: 1108

I know this is an old question, but I faced the exact same problem and it took me a while to solve. May this be of help to those coming from search engines.

I found 2 solutions (I ended up choosing the second one). Both allow you to use environment variables in docker to configure your API URL.

Solution 1 ("client"-side): env.js asset + sed

The idea is to have your Angular client load an env.js file from your HTTP server. This env.js will contain the API URL, and will be modifiable by your container when it starts. This is what you discussed in the question comments.

Add an env.js in your angular app assets folder (src/assets for me with angular-cli):

var MY_APP_ENV = {
  apiUrl: 'http://localhost:9400',
}

In your index.html, you will load your env:

<head>
  <meta charset="utf-8">
  <base href="/">
  <script src="env.js"></script>
</head>

In your environment.ts, you can use the variable:

declare var MY_APP_ENV: any;

export const environment = {
  production: false,
  apiUrl: MY_APP_ENV.apiUrl
};

In your NGINX Dockerfile do:

FROM nginx:1.11-alpine

COPY tmp/dist /usr/share/nginx/html
COPY run.sh /run.sh

CMD ["sh", "/run.sh"]

The run.sh script is where the sed magic happens:

#!/bin/sh

# API
/bin/sed -i "s|http://localhost:9400|${MY_API_URL}|" /usr/share/nginx/html/env.js

nginx -g 'daemon off;'

In your angular services, use environment.apiUrl to connect to the API (you need to import environment, see Angular 2 docs).

Solution 2 (purely server side): nginx proxy config + envsubst

I wasn't happy with the previous solution because the API URL needed to be from the host point of view, it couldn't use another container hostname in my docker-compose setup.

So I thought: many people use NGINX as a proxy server, why not proxy /api to my other container this way.

Dockerfile:

FROM nginx:1.11-alpine

COPY tmp/dist /usr/share/nginx/html
COPY frontend.conf.template /etc/nginx/conf.d/frontend.conf.template
COPY run.sh /run.sh

CMD ["/bin/sh", "/run.sh"]

frontend.conf.template:

server {
    listen       80;
    server_name  myserver;

    # API Server
    location /api/ {
        proxy_pass ${MY_API_URL}/;
    }

    # Main
    location / {
        root   /usr/share/nginx/html;
        index  index.html index.htm;
        try_files $uri$args $uri$args/ /index.html;
    }

    #error_page  404              /404.html;

    # redirect server error pages to the static page /50x.html
    #
    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   /usr/share/nginx/html;
    }
}

run.sh:

#!/bin/sh

# Substitute env vars
envsubst '$MY_API_URL' \
< /etc/nginx/conf.d/frontend.conf.template \
> /etc/nginx/conf.d/default.conf

# Start server
nginx -g 'daemon off;'

envsubt allows you to substitute environment variables in a string with a shell-like syntax.

Then use /api/xyz to connect to the API from the Angular app.

I think the second solution is much cleaner. The API URL can be the API docker container name in a docker-compose setup, which is nice. The client is not involved, it is transparent. However, it depends on NGINX.

Upvotes: 8

Related Questions