In an older post Build once deploy to multiple environments, I've already written on how to build an Angular application so it can be released in different environments.
But, in the last few iterations, Angular has evolved and has introduced new APIs to write applications. One of the big changes is the transition to standalone components and "standalone applications". This change affects the code examples that were used in the previous blog post, so this blog post is an updated version of the newer standalone Angular APIs.
When you're building an application it's probably a good idea to store some general settings in a central place.
An Angular application is no exception here.
As an example, let's say our Angular application communicates with a back-end API. Instead of hard-coding the URL in different places where a request is made (which is hard to maintain), it's preferable to store the URL outside of the application logic within a variable that can be referenced in the codebase. This makes it quick and easy to change in the future. Instead of replacing all occurrences across the entire codebase, you can simply update the URL setting once.
Other settings can consist of feature flags, theme settings, keys, ...
These settings are often referred to as the environment configuration because these describe all the settings/variables that are required for the application to function properly.
Because you store the environment configuration outside of your application code, this practice allows you to create different versions of your settings. That's a big benefit because you don't need to make any code-related changes before building/releasing the application.
You'll probably end up with a configuration variable for each of your environments (local, staging, production).
Within Angular, you don't need to roll your own implementation to cover this.
Angular already provides environtment files to manage environment-specific configuration variables.
build command you can provide a configuration, which is used to determine the corresponding environment file.
While environment files are built into Angular and are also encouraged in the documentation, I find it not desirable in most cases as it lacks some flexibility.
The drawback is that an environment file is part of the source code, and is thus included within the build output - you can find the settings of the environment file in the
main.js file within the
Because it's part of the build output it means that these variables cannot be changed after a build, e.g. during a release. Instead, you're required to create a new build for a different environment (or with updated variables). In other words, you're required to build the application multiple times, once for each environment you want it to be released to.
Or in a worst-case scenario, a new version of the application is released and the team detects or a customer experiences that there's an issue because a variable is wrong. Ideally, this doesn't happen, but trust me, it certainly will happen. When that's the case, a new build needs to be queued up before it can be released, which can take some time to fix the issue.
The solution link
In order to resolve this problem, the environment configuration needs to be extracted from the source code. This allows you to make changes to the environment variables during a release, either by swapping the entire file or by replacing specific properties (tokens) within the configuration. I don't mention the release step within this blog post as this can be different depending on your infrastructure.
As you'll see in the next sections, extracting the environment configuration can be done in multiple ways.
The solution described in Build once deploy to multiple environments uses a JSON file that's served as an asset. This keeps the configuration within the Angular project, alternatively, you can also expose the configuration via an API endpoint (just make sure that an anonymous user can access the endpoint). While this seems like two different things, the implementation from the Angular side is similar.
Before we make the changes, let's first define a type for the environment configuration so it can be accessed in a type-safe manner.
Additionally, also create an
InjectionToken to access the configuration within the components and services of the application using the Angular DI.
EnvironmentConfiguration is used to create the
appConfig isn't defined inline anymore, instead, a factory function is used to create the config. The factory function
createAppConfig receives the environment configuration as an argument (how we get access to the config is discussed later).
Based on the passed configuration, the
ApplicationConfig is created.
The example below simply makes use of the config object to provide it as the injection token. You can also grab specific properties of the config to create more dependencies.
Finally, retrieve the environment configuration using the
This needs to be done before bootstrapping the application, so the config can be passed to
environment-config.json file is just a simple JSON file containing the environment variables, in this case solely the
This configuration can be inlined within the
The important part is that the config is loaded before the application loads.
To use the config within Angular it needs to be shared/available, e.g. it can be assigned to a property on the
During the release, the
environment-config.js file can safely be replaced with the desired version as these files are just copied into the dist folder.
environmentConfiguration property of the window instance can be accessed while bootstrapping the Angular application.
Because the configuration is already available, we don't need to await the retrieval of this config file anymore. Instead, we can simply access the
window.environmentConfiguration to pass it to the
createAppConfig function, which remains the same as within the previous implementation using the JSON file.
Because the creation is now a synchronous step, this can be refactored further by leveraging the
factory method of the
ENVIRONMENT_CONFIG token behaves identically as it would be as it was explicitly defined in the application's root injector (and is thus also a singleton).
The changes to the
main.ts file can now be reverted to the original implementation.
Instead of the
createAppConfig factory method, the static
appConfig variable can be reused as well.
In both implementations, you can notice that the
environmentConfiguration is removed from the
This results that the config cannot be so easily accessed anymore from the
window (by a developer or a visitor), this forces a developer to access the environment variables via the token.
The discussed solutions all end up with the
ENVIRONMENT_CONFIG token that stores the environment variables of the application.
To access the variables within the application, inject the token within your components or services.
An application usually relies on some kind of environment configuration in order to function properly. Angular has the concept of environment files to manage environment-specific variables. However, because the environment files are part of the source code, this makes it impossible to reuse a build so it can be released in different environments.
To be flexible, a better solution is to extract the configuration out of the source code. In this blog post I've shown you two possible solutions:
- one creates a JSON file that contains the configuration, this file needs to be fetched to access the configuration;
windowobject before it can be accessed in the Angular context;
Personally, I prefer to use the JSON file because in my experience it's easier to edit/replace during a release.
Feel free to update this blog post on GitHub, thanks in advance!
I appreciate it if you would support me if have you enjoyed this post and found it useful, thank you in advance.