Angular Custom Environments Without Changing the Local Code or Repo.

Vugar Abdullayev
Geek Culture
Published in
3 min readMar 20, 2021

--

The more frustrating problem you face in development, the more amazing solution you end up with.

In this article we gonna talk about a scenario when we want to set up our custom environment by neither changing local code nor pushing to repository.

Problem

As you know Angular provides us environment files where we can store our config. But we still fill those files by writing statically our secret keys or settings. What if due to security, we do not want to share our secret keys to repository? Or what if we want our custom settings in environment and still don’t want to push those changes? The first ideal solution comes to mind is to set up environment file which retrieves this config from System Environment variables(.env) like below.

function getApiBasePath(): string {
return (window as any).config.API_BASE_PATH || ${process.env.DEFAULT_API_URL}';
}
export const environment = {
production: false,
API_BASE_PATH: getApiBasePath(),
MSAL: {
CLIENT_ID: ${process.env.MSAL_CLIENT_ID}',
AUTHORITY: ${process.env.MSAL_AUTHORITY}'',
},
debugStream: false,
};

Unfortunately, the above solution simply will not work. Because we are accessing system environment variables via process.env but process.env is available to Node applications, while an Angular application is not.

Solution

We can solve problem of reading system environment variables solution by using dotenv

  1. Install dotenv
npm i dotenv --save-dev

Dotenv is a zero-dependency module that loads environment variables from a .env file into process.env

2. Create new file(set-env.ts) in root of your Angular applicaiton.

This will let us process system environment variables (.env), create new or replace file called environment.custom.ts (targetPath propety).

const { writeFile } = require('fs');// Your environment.custom.ts file. Will be ignored by git.const targetPath = './src/environments/environment.custom.ts';// Load dotenv to work with process.envrequire('dotenv').config();// environment.ts file structureconst envConfigFile = `
function getApiBasePath(): string {
return (window as any).config.API_BASE_PATH || 'default-url';
}
export const environment = {
production: false,
API_BASE_PATH: getApiBasePath(),
MSAL: {
CLIENT_ID: '${process.env.MSAL_CLIENT_ID}',
AUTHORITY: '${process.env.MSAL_AUTHORITY}',
},
debugStream: '${process.env.DEBUG_STREAM}',
};
`;
writeFile(targetPath, envConfigFile, function (err) {
if (err) {
throw console.error(err);
} else {
console.log('Using custom environment');
}
});

Do not forget to setup your environment variables. For example, in Windows to set up MSAL_CLIENT_ID in above example, we run set MSAL_CLIENT_ID="your-id". It can be different in MacOS or Linux.

3. Add environment.custom.ts file to .gitignore in order to exlude our generated environment.custom.ts from being committed to repo.

# custom files
/src/environments/environment.custom.ts

4. By default Angular provides us two environments ( environment.ts and environment.prod.ts). Now we should let Angular know we have new custom environment file and the way to handle it( for serving ,building, testing). Add this bolded custom entry to angular.json for building (inside architect/build/configurations object)

"your-projectName": {
...
"architect": {
"build": {
...
"configurations": {
"production": {
"fileReplacements": [
{
"replace": "src/environments/environment.ts",
"with": "src/environments/environment.prod.ts"
}
]
},
"custom": {
"fileReplacements": [
{
"replace": "src/environments/environment.ts",
"with": "src/environments/environment.custom.ts"
}
]
},

}
...
}
...
}
...
}

5. Add custom entry for ng serve (inside architect/serve/configurations object)

"serve": {
"builder": "@angular-devkit/build-angular:dev-server",
"options": {
"browserTarget": "your-project-name:build",
"proxyConfig": "proxy.conf.json"
},
"configurations": {
"production": {
"browserTarget": "your-project-name:build:production"
},
"custom": {
"browserTarget": "your-project-name:build:custom"
}
}

},

6. Add custom entry to e2e (inside architect/e2e/configurations object)

"e2e": {
"builder": "@angular-devkit/build-angular:protractor",
"options": {
"protractorConfig": "e2e/protractor.conf.js"
"devServerTarget": "your-project-name:serve"
},
"configurations": {
"production": {
"devServerTarget": "your-project-name:serve:production"
},
"custom": {
"devServerTarget": "your-project-name:serve:custom"
}
}

}

7. Add or modify our code snippet to package.json to process our set-env.ts file.

{
...
"scripts": {
"ng": "ng",
"config": "ts-node set-env.ts",
"start": "npm run config && ng serve --configuration=custom",
"build": "npm run config && ng build --configuration=custom",
...
},
...
}

Here we goo , thats all for the problem. Lets summarize what happens here when we run npm run start.

ts-node will run set-env.tswhich will process our system environment variables and create new file which content will be simply the envConfigFile constant in set-env.ts file. Finally it will run ng serve which will use our new environment.custom.ts file as configuration.Also important, our commit is clean because we exluded our environment.custom.ts from changes.

More info: References 1 , 2 .

That’s all, hope you enjoyed the reading and benefit from it.☕

Follow me on Twitter

--

--