sandeep krishna
sandeep krishna

Reputation: 475

Reading server environment variable in Angular at runtime

I am new into angular. I have deployed an angular app into a docker container in aws. The app needs to connect to aws s3 bucket. I dont need to hardcode the aws key so I setup an environment variable in the docker. the angular is deployed such that the dist folder from the ng build is served by an nginx docker container.

Is it possible to read the server environment variable by the angular once the contents from the dist folder is served into the server? like env function in php and nodejs

Upvotes: 4

Views: 21633

Answers (3)

Ben H
Ben H

Reputation: 133

I know this is an old question, but I wanted to add a potential option for anyone searching for this.

If you are deploying your Angular app in a Docker container using NGinx or something similar, I used a tool called confd by @kelseyhightower here https://github.com/kelseyhightower/confd .

Basically modifying index.html so it had confd template variables in it, that create a "config" object that is then accessible in your Angular code. You could then use the values in that "config" object to set your environment.ts values. Confd would need to be set up to run on container start.

This would be added to the end of your Dockerfile Dockerfile config

This would be added to your index.html file. index.html config

This is the start script for the container. I copy it into /usr/local/bin as part of my Dockerfile steps. start.sh script

Access the values in your environment.ts file environment.ts

Worked well for me so far, but you may need to find an alternate solution for deployments that are not Container based.

Upvotes: 2

code-gorilla
code-gorilla

Reputation: 2438

Unfortunately there is no way to read an environment variable in Angular similar to mechanisms that server frameworks like nodejs or Spring Boot offer.

You could however load the environment from the server before the Angular app bootstraps (please read the drawbacks below before taking this path).

Load environment variables from a server:

  • Remove the bootstrap component from your root module (e.g. AppModule) so that it is not bootstrapped automatically
  • Create a singleton service in root that loads the environment variables, e.g:
import { Injectable } from "@angular/core";

@Injectable({ providedIn: "root" })
export class EnvService {
  public myEnv: string;

  fetchEnv() {
    // This has to retrieved from some server.
    return fetch("some-url").then(env => {this.myEnv = env.myEnv; return Promise.resolve("env loaded");};
  }
}
  • Delay the bootstrap in your root module until the environment is loaded:
@NgModule({
  imports: [BrowserModule, FormsModule],
  declarations: [AppComponent, HelloComponent]
})
export class AppModule {
  constructor(private envService: EnvService) {}
  ngDoBootstrap(app) {
    this.envService.fetchEnv().then(() => app.bootstrap(AppComponent));
  }
}

This approach has a couple of drawbacks:

  • The url from where to retrieve the env settings has to be known before loading the other env variables
  • The Angular app will not boot if the environment variables can't be loaded. As you are using Docker, you could host a small web server inside the Docker container that provides the environment variables on a dedicated endpoint for the Angular app. However this might come with its own problems, as this makes creating your Angular Docker container more complicated.

I would therefore recommend configuring the env variables during the build.

There are 2 ways you can set the environment variables during the build:

1) Set the environment variable in the build using a custom webpack config

See this article.

You can specify a custom webpack config in your angular.json:

"architect": {
        "build": {
          "builder": "@angular-builders/custom-webpack:browser",
          "options": {
            "customWebpackConfig": {
              "path": "extra-webpack.config.js"
            },
...

The webpack config file would look like this:

const webpack = require('webpack');

module.exports = {
  plugins: [
    new webpack.DefinePlugin({
      'process.env': {
        // Here you can set your env variables.
        SERVER_URL: JSON.stringify(process.env.SERVER_URL)
      }
    })
  ]
}

This method requires some more dependencies:

@angular-builders/custom-webpack
dotenv

2) Dynamically create an environment file during build

See e.g. the automatically generated src/environments/environment.prod.ts. You could generate this file with the correct environment values or replace placeholders in this file during the build.

Edit: See also The Fabio's answer. You probably don't want your users to be aware of the S3 bucket anyway and should route your request over a web server to the bucket if possible.

Upvotes: 8

The Fabio
The Fabio

Reputation: 6260

php and nodejs run on the server side, that's why they have access to the server environment variables.

Anything your angular app can read is exposed to the client, as Angular runs in the browser. I would assume you don't want your AWS keys floating around on the client where any user could ready them. This exposes your back-end to malicious use.

You can always create an endpoint in the server app your angular app is talking to expose the server environment variables if you really choose to go that path.

The more common pattern is to have your server endpoints serving data to angular instead of its environment variables:

  1. Your angular service calls an enpoint from the serve;
  2. the server using its configurations and variables get the data;
  3. the server responds to the angular service call with the data;

I am overly simplifying the server interaction as that is something I would advise you to read in separated from this question. This post should help you in getting started with back-end / front-end separation.

Upvotes: 1

Related Questions