DEV Community

Cover image for Using shared settings between multiple environments in Angular πŸš€
Nikos Anifantis
Nikos Anifantis

Posted on

Using shared settings between multiple environments in Angular πŸš€

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

Create new staging env file

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>",
};
Enter fullscreen mode Exit fullscreen mode

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"
            }
          }
        }
      }
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

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
};
Enter fullscreen mode Exit fullscreen mode
// environment.prod.ts

export const environment = {
  production: true,
  environmentName: "production",
  apiUrl: "https://my-api.com/api", // common setting
};
Enter fullscreen mode Exit fullscreen mode
// environment.staging.ts

export const environment = {
  production: true,
  environmentName: "staging",
  apiUrl: "https://my-api.com/api", // common setting
};
Enter fullscreen mode Exit fullscreen mode

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.

Create new staging env file

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",
};
Enter fullscreen mode Exit fullscreen mode

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);
Enter fullscreen mode Exit fullscreen mode

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);
Enter fullscreen mode Exit fullscreen mode
// 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);
Enter fullscreen mode Exit fullscreen mode

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;
    // ...
  }
}
Enter fullscreen mode Exit fullscreen mode

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";
Enter fullscreen mode Exit fullscreen mode

❗ 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
Enter fullscreen mode Exit fullscreen mode
# Compiles an Angular app into an output directory
$ ng build --configuration staging
Enter fullscreen mode Exit fullscreen mode

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)

Collapse
 
hanct profile image
Hanster

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.

Collapse
 
kinaski profile image
kinaski

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.

Collapse
 
nikosanif profile image
Nikos Anifantis

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

Collapse
 
rmcsharry profile image
Richard McSharry • Edited

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#...

Collapse
 
edpressoc profile image
EdPresso Community

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.