The problem π
When we create a new Angular project, by default Angular CLI creates two environment files for development
and production
.
But in real world software development workflow, we may need more custom environments (e.g. staging
, qa
). Moreover, there are some environmental variables that are common between these environments. For example, API URL to fetch data from server. Thus, we must find a way to share the base settings across all environments and override them as needed.
In this article, we are going to learn:
- How to add custom environments (e.g.
staging
) - How we can share some common settings between environments
Create a custom environment π
Open your Angular project and create a new file under environments
folder. In this example, we create a new environment for staging (environment.staging.ts
file).
π TIP: The name of the file should follow the naming convention:
environment.<your-env-name>.ts
Great, let's add some configuration in our new env file...
// environment.staging.ts
export const environment = {
production: true,
environmentName: "staging",
apiUrl: "<your-api-url-here>",
};
Update angular.json
config file π
Once you have created the new environment file, you have to specify file replacements for different environments. In your angular.json
file find the following paths:
projects.<your-project-name>.architect.build.configurations
projects.<your-project-name>.architect.serve.configurations
Update the sections as the following example:
// angular.json
{
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
"version": 1,
"newProjectRoot": "projects",
"projects": {
"<your-project-name>": {
// ...
"architect": {
"build": {
//...
"configurations": {
//...
"staging": {
"fileReplacements": [
{
"replace": "src/environments/environment.ts",
"with": "src/environments/environment.staging.ts"
}
]
}
}
},
"serve": {
//...
"configurations": {
"staging": {
"browserTarget": "<your-project-name>:build:staging"
}
}
}
}
}
}
}
The problem is still out there π₯
Although we have created a new custom environment, in a typical project setup we may have duplication of environmental settings in which the values may be shared between them.
An example is depicted below where the API URL is common for all environments:
// environment.ts
export const environment = {
production: false,
environmentName: "development",
apiUrl: "https://my-api.com/api", // common setting
};
// environment.prod.ts
export const environment = {
production: true,
environmentName: "production",
apiUrl: "https://my-api.com/api", // common setting
};
// environment.staging.ts
export const environment = {
production: true,
environmentName: "staging",
apiUrl: "https://my-api.com/api", // common setting
};
In this example, the common settings is not a real problem, since there is only one shared value between the existing environments.
But... Imagine a real world Angular project which has at least 4-5 environments and a bunch of settings that developers have to maintain. This is really a problem, believe me... ππ₯
Create a common environment π¦π₯
Letβs make our life easy by using a base configuration file for common settings across all environments.
First, in your project create a new file with name environment.common.ts
under the environments
folder.
The next step is to add all base configuration in there...
// environment.common.ts
export const commonEnv = {
production: false,
environmentName: "development",
apiUrl: "https://my-api.com/api",
};
Last step is to change the existing environment files by importing the common file and override only the necessary settings.
In this example we don't need to override anything for the default environment (development
).
// environment.ts
import { commonEnv } from "./environment.common";
const env: Partial<typeof commonEnv> = {};
// Export all settings of common replaced by dev options
export const environment = Object.assign(commonEnv, env);
But, for production
and staging
we have to override some settings from the base.
// environment.prod.ts
import { commonEnv } from "./environment.common";
const env: Partial<typeof commonEnv> = {
production: true,
environmentName: "production",
};
// Export all settings of common replaced by dev options
export const environment = Object.assign(commonEnv, env);
// environment.staging.ts
import { commonEnv } from "./environment.common";
const env: Partial<typeof commonEnv> = {
production: true,
environmentName: "staging",
};
// Export all settings of common replaced by dev options
export const environment = Object.assign(commonEnv, env);
We use the Object.assign()
method that allows you to copy all enumerable own properties from one or more source objects to a target object, and return the target object.
π Pro Tip
Instead of
Object.assign()
method, you can use the spread operator.export const environment = { ...commonEnv, ...env };
As an alternative, if you have more advanced setting with nested properties, I recommend to use
merge
function from lodash library.export const environment = _.merge(commonEnv, env);
There is one more extremely useful trick here to preserve typings with typescript.
We use Partial<typeof commonEnv>
in order to help us to track the properties of the common environment and override them correctly.
Use environment settings inside project β¨
Great! We have created multiple environments and now is time to use them in our project.
You can import environment settings in your services/components like the following example:
import { Component } from "@angular/core";
import { environment } from "src/environments/environment"; // here is the import
@Component({
selector: "app-root",
templateUrl: "./app.component.html",
styleUrls: ["./app.component.css"],
})
export class AppComponent {
private apiUrl: string;
constructor() {
this.apiUrl = environment.apiUrl;
// ...
}
}
Please be careful! Don't import your settings from the direct environment file!
For example:
// DO NOT
import { environment } from "src/environments/environment.staging";
// DO
import { environment } from "src/environments/environment";
β Attention
We saw how to import environment settings in our project...
But it's NOT good idea to import them directly at your components or services.I suggest to create a new Angular service (e.g.
ConfigService
) and handle all settings from there by injecting it in your components.
Thus, you will have a cleaner and more maintainable code.
Run/Build your project π¦
We are reaching the end, the hard work is done!
Now it's time to run and build your application for targeted environment by specific --configuration
parameter with Angular CLI.
In order to build our application in staging
mode, run one of the following commands:
# Builds and serves your app, rebuilding on file changes.
$ ng serve --configuration staging
# Compiles an Angular app into an output directory
$ ng build --configuration staging
Conclusion β
Hooray! Weβre done! π
I hope you enjoyed this guide and will make your applications even more clean and maintainable by splitting environments to separate reusable configuration files! This approach can help in an excellent way in the enterprise organization setting!
Please support this article with your β€οΈ π¦π to help it spread to a wider audience π.
Top comments (5)
May i ask, is there any concern if i simply use the default environment file (environment.ts) to contain the common settings, rather than having to create another environment.common.ts file?
Cos in spring boot, the default configuration file (resource.properties) can also considered to be the common settings. If a setting is NOT found in resource-dev.properties, the app will automatically fallback and check for that setting in resource.properties. Wish spring boot has this auto-fallback feature, rather than having to implement ourselves.
Is it possible to create extra .prod environment and to run command like:
"ng build --prod --configuration new_environment" .
I want to have optimised files.
Thanks for your comment @kinaski !
Unfortunately as far I know, you cannot do this! If you can see the official documentation, setting the
--configuration
option explicitly overrides the--prod
flag.For more information, see: angular.io/cli/build#options
Yes you can do this. Just don't use the --prod flag.
"ng build --configuration my_special_config"
in angular.json add "my_special_config"
If you wanted to apply everything that you've configured already for the production build, but overwrite/add some settings then still create it in angular.json and then:
"ng build --configuration production, my_special_config"
See angular.io/guide/workspace-config#...
This is fantastic! Would you be interested in crossposting to Edpresso by Educative?
Reach out to edpresso@educative.io for more information.
Some comments may only be visible to logged-in visitors. Sign in to view all comments.