Angular CLI and OS Environment Variables
Trying to google for help about how to use environment variables with Angular CLI is kind of a nightmare. I found it to be so because the Angular CLI’s answer to this seems to be naive to the fact that one should not store sensitive credentials in a VCS for all devs to see. Now of course, there’s always
ng eject, but the Angular CLI is actually really, really nice! With its simplicity and its conventions, it is, in this programmer’s opinion, a welcome respite from the chaos of modern front-end development. So what does one do?
Let me answer that in two parts: firstly by describing the convention, then the problem as I experienced it, along with a the solution that is straight forward, and doesn’t flaunt said conventions that much. But first, the TL;DR:
environment.dev.ts, is the config for your
npm install -g yargs dotenv
- Add a
.env(not committed to version control) with your sensitive environment variables as per the dotenv README
- Add a script (I called it
set-env.ts) that to dynamically generate your environment-specific file using environment variables (now available via
process.envthanks to dotenv)
- Update the
set-env.tsfirst, then the respective
Here’s a working example (output of
ng new + these changes): https://github.com/natchiketa/angular-cli-envvars
The Angular CLI way of exposing global configuration properties on a per-environment basis revolves around environment-specific files, named something like
environment.__YOUR_ENVIRONMENT_NAME__.ts. These are basically just TypeScript files that
export an object with whatever your environment-specific properties might be, like this:
So, to start out, you’ve got this one property,
production, which is
false by default. Then, as the comments explain, for your production environment you would start out with something like this:
These files are, by convention, kept in
src/environments/ and declared in
apps.environments like so:
And I’ll now say ‘Convention’ with a capital ‘C’ because it’s ‘convention’ in literally the same sense as in the Rails world, of which the Angular CLI is a spiritual successor, being based on Ember CLI—that is to say it inherits the Rails mantra “Convention Over Configuration”.
Of course, I wouldn’t want to put anything like say, an AWS access key/secret in these files, especially for production, if the file is to be committed to a repository shared by many developers.
You might think “Maybe you could just not commit those files? Keep them per environment and stage them in the build process.” In my experience, this tends to be a pain in the ass—especially if you have more environments (a shared, hosted dev, QA, ‘pre-prod’, and/or one or more spun-up test environments for CI, etc, ad infinitum, ad nauseum).
The more painless and, in my opinion, the more elegant solution, is to use environment variables. Essentially, let OSes do this thing at which they have always been quite good.
But unfortunately, simply trying to run dotenv’s
config function doesn’t seem to work with
environment.*.ts files—this would certainly be the most pragmatic way to do it. But when I tried to do this, everything would just come up
So, accepting that the Angular CLI seems to just want these files to be static, and sort of bites it’s thumb at my attempts to do dynamic something-or-other, I figured OK, I’ll just dynamically generate these with a regular-ol’ script, before each
ng command, and wrap it all in
Reglah-Ass Script to the Rescue
So, as per the TL;DR overview at the top—you’ll want
dotenv. I’ve already pretty much described what
dotenv brings to the table, but to be clear: dotenv essentially allows you do specify environment variables in a file. I know hearing me say this may now be causing you to make a certain face, popularized by a certain Jackie Chan meme, but bear with me:
The file dotenv reads,
.env, won’t be committed. It’s just that, when you’re in your local dev environment, it’s kind of a pain to have to actually set environment variables in your OS, especially when you might have your hand in a bazillion different codebases at any given time. This use case, and this use case alone, is why dotenv is included. For all other environments—which will most likely be using a more automated deployment process, and (ideally) have much more guarded shell access, regular old environment variables will be the way to go.
yargs is the one that is used on all environments, purely so that the script we’re now creating—which will run first, and neither knows nor cares about Angular CLI—can know which environment we’re going to
ng build, etc.
The idea is to be able to do this:
# Dynamically generate a config and write it
# to src/environments/environment.dev.ts
ts-node set-env.ts --environment=dev
# Note: ts-node is the TypeScript Node.js REPL,
# and should already be installed by default
# by the Angular CLI.
# Now that that's generated, use any
# Angular CLI command, for example:
ng serve --environment=dev
And in order to avoid this verbosity, wrap it in
npm scripts. For example:
"config": "ts-node ./scripts/set-env.ts",
"start": "npm run config -- --environment=dev && ng serve --environment=dev",
"build": "npm run config -- --environment=prod && ng build --environment=prod",
"test": "ng test",
"lint": "ng lint",
"e2e": "ng e2e"
NOTE: The extra
npm run config is so that the argument gets passed to the script, not to
Oh, right—the script! It goes something like this:
I would love any suggestions—please create an issue on the repo! This initial implementation isn’t 100% baked just yet. When it is though, I don’t see anything reason it can’t be added to the Angular CLI. Anything anyone can suggest/contribute to make this PR-ready would be most welcome!