Trying to set environment variables in appSettings.json - Angular and Typescript

Viewed 2363

I am basically trying to set the API URL path in appSettings so I can change back and forth from prod and dev environments easily.

This is my appSettings.json file. apiURL is the variable I am trying to set and get.

{
 "Logging": {
"LogLevel": {
  "Default": "Information",
  "Microsoft": "Warning",
  "Microsoft.Hosting.Lifetime": "Information"
}
  },
 "AllowedHosts": "*",
 "apiURL": "http://10.241.2.68:8132/api"
}

Here is my page that is trying to access the environment variable that I set in appSettings.json.

import { Injectable } from '@angular/core';
// import { Defendant } from 'src/app/Models/Defendant';
import { Charge } from 'src/app/Models/Charge';
import { DefendantItem } from '../Models/DefendantItem';
import { Defendant_Charge } from '../Models/Defendant_Charge';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
import { AppConfiguration } from "read-appsettings-json";  
@Injectable({
providedIn: 'root'
})
export class ChargeService {
//console.log(AppConfiguration.Setting().apiURL);
////////////////////////////////////////////////////////////////////
//  below is where I am trying to reference the environment variable
//  the entire program bombs and displays "GET \" on the page.
ChargeControllerUrl = AppConfiguration.Setting().apiURL + "/Charges";
//ChargeControllerUrl = "http://10.241.2.68:8132/api/Charges";
}
3 Answers

I would suggest not using environment.ts since this is a build time configuration.

What you are actually looking for is some way to fetch your app-settings.json file depending on the environment, at run time. This means you can package your code and promote the packaged code through your environments, without rebuilding.

Angular has something called APP_INITIALIZER which you can use to achieve this.

  1. Set up your app-settings.json (app-settings.qa.json, app-settings.prod.json) in assets/config folder

  2. Configure an AppConfigService angular service.

//app/services/app-config.service.ts
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';

@Injectable({
  providedIn: 'root'
})
export class AppConfigService {

  private appConfig: any;
  private http : HttpClient;

  constructor(http: HttpClient) {
    this.http = http;
  }

  loadAppConfig() {
    return this.http.get('/assets/config/app-settings.json')
      .toPromise()
      .then(config => {
        this.appConfig = config;
      });
  }

  get apiBaseUrl() : string {
    return this.appConfig.apiBaseUrl;
  }
}

  1. Configure the AppService using the APP_INITIALIZER as the injection token in your AppModule
import { BrowserModule } from '@angular/platform-browser';
import { NgModule, APP_INITIALIZER } from '@angular/core';

import { AppComponent } from './app.component';
import { HttpClientModule } from '@angular/common/http';
import { AppConfigService } from './services/app-config/app-config.service';

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    HttpClientModule
  ],
  providers: [
    {
      provide : APP_INITIALIZER,
      multi : true,
       deps : [AppConfigService],
       useFactory : (appConfigService : AppConfigService) =>  () => appConfigService.loadAppConfig()
    }

  ],
  bootstrap: [AppComponent]
})
export class AppModule { }

Now you can use this service to fetch the baseURL using dependency injection

Below is an example service, what yours might look like. Note the constructor and the apiUrl private class variable

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
import { IempList } from '../interfaces/iemp-get-res';
import { map } from 'rxjs/operators';
import { AppConfigService } from 'src/app/services/app-config/app-config.service';

@Injectable({
  providedIn: 'root'
})
export class EmpService {

  constructor(private httpClient: HttpClient, private appConfigService: AppConfigService) { }

  private apiUrl = this.appConfigService.apiBaseUrl + '/api/employee';

  public getEmp(): Observable<IempList> {
    return this.httpClient.get(this.apiUrl)
      .pipe(map((res: IempList) => res));
  }

  public deletEmp(id): Observable<any> {
    return this.httpClient.delete(this.apiUrl + '/' + id)
      .pipe(map((res: any) => res));
  }

  public createEmp(formData): Observable<any> {
    return this.httpClient.post(this.apiUrl + '/create', formData.data)
      .pipe(map((res: any) => res));
  }

  public editEmp(formData): Observable<any> {
    return this.httpClient.post(this.apiUrl + '/update', formData.empData)
      .pipe(map((res: any) => res));
  }
}

from here, you will need to use your deployment method (azure devops pipelines, for example) to rename the appropriate file in the assets folder according to your env

qa: rename app-settings.qa.json -> app-settings.json prod: rename app-settings.prod.json -> app-settings.json

And with that you will have a runtime app config

As stated in comment, appsettings.json is not for angular application. here you can go through this tutorial to find necessary steps for using enviornment. here are some quick steps:

import { environment } from './../environments/environment';
...
const baseUrl: string = environment.baseUrl;

where environment.ts may look like

export const environment = {
  production: false,
  baseUrl: "http://localhost:45644/api/",
};

live example here

You should follow these steps:

  1. Create a .env file in the root directory of your project and add environment-specific variables on new lines in the form of NAME=VALUE like this:
API_URL=http://localhost:45644/api/
  1. Use dotenv's tools that loads environment variables from a .env file into process.env object

  2. Install custom-webpack as a dev dependency:

npm install -D @angular-builders/custom-webpack
  1. Modify your Angular configurations – angular.json – to use the new builder (custom-webpack)
"projects": {
    "name-of-the-project": {
      //...
      "architect": {
        "build": {
          "builder":"@angular-builders/custom-webpack:browser"_,
          "options": {
            "customWebpackConfig": {
                "path":"custom-webpack.config.js"
            },
            //...
        //...
    } }
  1. Then, you are going to create your custom-webpack.config.js file at the root of your Angular Workspace and add the following content to retrieve your environment variables and pass them to your builder:
const webpack = require('webpack')
    require('dotenv').config()
    module.exports = {
      plugins: [
        new webpack.DefinePlugin({
          $ENV: {
            API_URL: JSON.stringify(process.env.API_URL),
          },
        }),
      ],
    }
  1. Finally to use your environment variables in Angular,you are going to add some typings, so that you can reference to your $ENV variable from webpack above within our Angular app.
  • First, let’s create a typing.d.ts file inside your src directory at the root of your workspace and add the following content:
declare var $ENV: Env;
    
    interface Env {
      API_URL: string;
    }
  • Then, inside the environment.ts, you need to read the environment variables passed to your application, so that your environment.ts would like this:
export const environment = {
  production: true,
  environment: $ENV.API_URL,
};
  1. And for using your environment variables on your components:
import { environment } from './../environments/environment';
...
public environment = environment.environment
Related