Angular [Universal] runtime configuration
When you generate an Angular web application using Angular CLI, a pair of files named environment.ts
and environment.prod.ts
are created. The former holds the configuration for your development environment, whilst the latter is for production.
In fact, you can target different configurations to serve the settings in any number of different environments by creating and adapting a file for each of those targets. For example, environment.uat.ts
and environment.staging.ts
, might specifically configure the application to run in a UAT or Staging environment respectively.
Unfortunately, the task of creating a working application for a different target environment doesn't stop simply with the creation of a new environment file.
Properly serving and creating a build for the correct target environment, requires updates to the "scripts" element in package.json
file, so the application knows what settings are applicable when you want to run or build in say, the UAT configuration.
"scripts": {
"ng": "ng",
"test": "ng test",
"start": "ng serve",
"start:uat": "ng serve --configuration uat",
"build": "ng build",
"watch": "ng build --watch --configuration development",
},
In angular.json
we also have to update every "configurations" element so the application has the correct settings for the desired environment.
"configurations": {
"production":
"fileReplacements": [
{
"replace": "src/environments/environment.ts",
"with": "src/environments/environment.prod.ts"
},
...
],
},
"staging": {
"fileReplacements": [
{
"replace": "src/environments/environment.ts",
"with": "src/environments/environment.staging.ts"
},
...
],
},
"uat": {
"fileReplacements": [
{
"replace": "src/environments/environment.ts",
"with": "src/environments/environment.uat.ts"
},
...
],
},
"development": {
"buildOptimizer": false,
"optimization": false,
...
}
},
There are multiple "configurations" elements in angular.json
and the more environments that need to be set up for the application, the larger and more complex this file becomes.
However, the biggest issue lies in the implementation of continuous integration-continuous deployment (CI-CD): having configuration as TypeScript files, means that the configuration is part of the application source code. In other words we are limited to compile-time configuration.
Therefore, to run the configuration for a given environment, we have to create a specific build. E.g. ng build --configuration uat
or ng build --configuration staging
.
The build created with --configuration uat
for example, cannot be deployed to Staging or Production.
Runtime configuration
Ideally what we want is to create just one build - a single artifact of the transpiled JavaScript - that can be deployed to any environment, UAT, Staging or Production. The only thing we need do to modify the behaviour of the application for each environment is apply an environment-specific configuration when the application initialises.
This is runtime configuration.
When we use exactly the same build artifact in all environments, we can be confident that the only variation we are porting as we move from one environment to another, is configuration. If something goes wrong in one environment when it is proven to work in another, then we can be sure there's a problem with configuration or the deployment target itself.
This is preferable to compile-time configuration, where every build artifact is potentially different. When something goes wrong in an environment, we have to question not just the configuration and environment, but also the build itself.
The solution
I found a few articles to help me make the transition to runtime configuration - giving me two methods. I created simple Angular projects to illustrate how to implement both these techniques, which you can find at github.com/crucisco/uni-with-config.
As an added bonus, because I needed it for my own website, I extended the techniques so they can be used with Angular Universal - using both SSR (server-side rendering) and pre-rendering.
My next few articles will be an in-depth dive into each technique!