@elemental-concept/env-bakery
v18.0.0
Published
Loads environment variables from .env file into Angular environment
Downloads
421
Readme
EnvBakery
EnvBakery is an extension for Angular framework that loads environment variables from an .env
file (located
in /assets
) into Angular environment on application start-up. Normally Angular environment is baked in into the
application during build stage and all Angular environments are part of source code which
breaks The Twelve-Factor App methodology.
EnvBakery also provides direct access to variables specified in an .env
file.
Some environments might not allow file injection and will instead rely on build process to consume environment variables
from OS. parbake
CLI utility is included for such cases.
EnvBakery is using .env
parser extracted from dotenv by Scott Motte. Sadly,
dotenv tries to access Nodejs APIs even when only using its parser. This obviously fails when used in a browser
environment. So the parser code had to be extracted to avoid dotenv dependency entirely.
Installation With Angular CLI
EnvBakery comes with experimental installation schematic which should be invoked through Angular CLI. This method will install the library itself and will try to update the following files:
src/environments/environment.ts
src/main.ts
angular.json
It will also create boilerplate src/assets/.env
and src/assets/.env.example
files.
Before using this installation method, make sure that you have committed all file changes. If you encounter any issues - please create a bug report.
To install using Angular CLI run:
$ ng add @elemental-concept/env-bakery
Once it finishes, feel free to remove unneeded environment files.
Manual Installation
Install the library
# With npm
$ npm i @elemental-concept/env-bakery
# With Yarn
$ yarn add @elemental-concept/env-bakery
Create .env file inside /assets
Create an .env
file in the /assets
folder of your application. Usually .env
file is created in the root folder of
the project, but a single Angular project might contain multiple applications, thus it is important to have per
application .env
file located inside /assets
folder of each application. Make sure to add .env
files
to .gitignore
- environment files should NEVER be submitted to the code repo!
Add environment-specific variables on new lines in the form of NAME=VALUE
. You can also use comments.
# Angular production flag
PRODUCTION=false
# Sample Firebase config
FIREBASE_API_KEY=api-key
FIREBASE_AUTH_DOMAIN=my-cool-app.firebaseapp.com
FIREBASE_PROJECT_ID=my-cool-app
FIREBASE_STORAGE_BUCKET=my-cool-app.appspot.com
FIREBASE_MESSAGING_SENDER_ID=123456789
FIREBASE_APP_ID=1:000:web:999
FIREBASE_MEASUREMENT_ID=G-ABCDEF
Convert Angular environment into function
Due to the way modules are bundled and imported, it is required to convert Angular environment from a static constant
into a function. Open your src/environments/environment.ts
and do the following change (don't worry about your
environment.prod.ts
at the moment):
export const environment = () => (
{
production: false,
firebase: {
apiKey: 'api-key',
authDomain: 'my-cool-app.firebaseapp.com',
projectId: 'my-cool-app',
storageBucket: 'my-cool-app.appspot.com',
messagingSenderId: '123456789',
appId: '1:000:web:999',
measurementId: 'G-ABCDEF'
}
}
);
Make sure to update all of your code which uses environment
as a constant to use it as a function. Don't change
your main.ts
yet though.
Inject .env variables into Angular environment
Import getEnv()
from @elemental-concept/env-bakery
and replace static environment property values with getEnv()
calls:
import { getEnv } from '@elemental-concept/env-bakery';
export const environment = () => (
{
production: getEnv('PRODUCTION').boolean(),
firebase: {
apiKey: getEnv('FIREBASE_API_KEY').string(),
authDomain: getEnv('FIREBASE_AUTH_DOMAIN').string(),
projectId: getEnv('FIREBASE_PROJECT_ID').string(),
storageBucket: getEnv('FIREBASE_STORAGE_BUCKET').string(),
messagingSenderId: getEnv('FIREBASE_MESSAGING_SENDER_ID').string(),
appId: getEnv('FIREBASE_APP_ID').string(),
measurementId: getEnv('FIREBASE_MEASUREMENT_ID').string()
}
}
);
Check API section below on how to use getEnv()
.
Update app.module.ts / main.ts
Add APP_INITIALIZER
to your providers in app.module.ts
or main.ts
(if using standalone components):
import { APP_INITIALIZER, NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { bakeEnv } from '@elemental-concept/env-bakery';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
// Call bakeEnv to load .env file on start up
function initializeApp() {
return bakeEnv(() => import('../environments/environment'));
}
@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
AppRoutingModule
],
providers: [
// Add APP_INITIALIZER to load .env before the app starts
{
provide: APP_INITIALIZER,
useFactory: () => initializeApp,
multi: true
}
],
bootstrap: [AppComponent]
})
export class AppModule {
}
Remove environment.prod.ts
You don't need environment.prod.ts
anymore when using @elemental-concept/env-bakery
. Open angular.json
and remove
environment.prod.ts
and environment.ts
replacement. You can then remove environment.prod.ts
file from your project
entirely.
{
"configurations": {
...
"production": {
...
// Remove environment.prod.ts replacement from this array
"fileReplacements": []
}
...
}
}
Set up your deployment system
Your deployment system should be aware of these changes. Please refer to the documentation of your deployment system for exact steps on how to inject custom files into your project.
If using Docker, then volumes are usually used for that:
$ docker run -v $(pwd)/.env:/root/app/dist/assets/.env my_docker_image
parbake CLI utility
Some deployment environments might not allow file injection and will instead rely on build process to consume
environment variables from OS. Netlify is a good example of such deployment environment. It
only allows to specify environment through its web interface and then it runs $ npm build
on each deploy. parbake
CLI utility is included for such cases.
The easiest way to use parbake
is to create a build hook in your package.json
:
{
"scripts": {
"prebuild": "parbake src/assets/.env PRODUCTION,API_BASE_URL,FB_API_KEY,FB_API_SECRET",
"build": "ng build --prod"
}
}
It will consume environment variables defined in a host OS and will create an .env
file with their contents. To avoid
exposing the whole of OS environment to the public, parbake
requires a whitelist, which can either be specified as a
command line argument or loaded from a JSON configuration file. parbake
can also be called through npx
.
Usage:
$ parbake [output] [whitelist]
$ npx @elemental-concept/env-bakery [output] [whitelist]
$ parbake [output] --config=[filename]
$ npx @elemental-concept/env-bakery [output] --config=[filename]
$ parbake [output] --json=[environmentJson]
$ npx @elemental-concept/env-bakery [output] --json=[environmentJson]
Whitelist is a comma-separated list of environment variable names.
Configuration file must be in JSON format, should contain a single object with a single property called whitelist
,
which in turn should contain a list of strings. For example:
{
"whitelist": [
"PRODUCTION",
"API_BASE_URL",
"FB_API_KEY",
"FB_API_SECRET"
]
}
Alternatively you can specify required environment variables as a single JSON string using --json
argument.
Make sure to escape double quotes correctly! Example:
$ npx @elemental-concept/env-bakery src/assets/.env --json='{\"PRODUCTION\": \"true\"}'
You can also install EnvBakery as a global package and use parbake
locally to dump your environment into a file.
API
.env
variables should be accessed through getEnv()
after successful bootstrap. You can either inject them into you
Angular environment
or use getEnv()
directly if needed.
getEnv()
getEnv(key: string): EnvConverter
- returns an instance of EnvConverter
bound to an environment variable specified
by key
.
EnvConverter
Provides a set of wrapper functions to access environment variables as strings, numbers, booleans and arrays of strings.
If environment variable is not specified, default value will be returned. If you need to access an environment variable
without any modifications, use raw()
method.
raw(): any
- returns environment variable as-is without any modifications. If environment variable is not present
in .env
file then undefined
will be returned.
number(defaultValue = 0): number
- returns environment variable as a number
primitive. String values will be
converted using parseFloat()
. defaultValue
will be returned instead of non-numeric values (including undefined
)
and NaN
.
string(defaultValue = ''): string
- returns environment variable as a string
primitive. defaultValue
will be
returned instead of non-string values (including undefined
).
array(separator = ',', defaultValue: string[] = []): string[]
- returns environment variable as an array of strings.
Environment variable will be split by separator
. defaultValue
will be returned in case of any errors.
boolean(truthyValues = [ 'true', 't', '1', 'on', 'enable', 'enabled', 'yes' ], defaultValue = false): boolean
-
returns environment variable as a boolean. Any string value present in truthyValues
will return true
, any other
string value will return false
. Non-string values will return defaultValue
.