skip to content

In an Angular CLI project, how do you add a staging build configuration with its own environment file, and what does fileReplacements do?

level: middleimportance: must knowfreq 65%

answer

  1. one file imported, another compiled
  2. generate the environments folder
  3. a named configuration per stage
  4. serve needs its own entry
  5. values end up in the bundle

basics

~10 s

Add a staging entry under the Angular CLI build target's configurations whose fileReplacements swaps src/environments/environment.ts for environment.staging.ts at compile time, give serve a matching buildTarget, and build with ng build -c staging.

solid answer

~40 s

Run `ng generate environments` once: it creates `src/environments/environment.ts`, used by the default `production` configuration, plus `environment.development.ts` with a matching `fileReplacements` entry in the `development` configuration. For staging, add `environment.staging.ts` exporting the same shape and a `staging` configuration under the `build` target with `"fileReplacements": [{ "replace": "src/environments/environment.ts", "with": "src/environments/environment.staging.ts" }]`. Application code always imports the original `environment.ts`; during a staging build the compiler loads the staging file in its place, so the values are baked into the bundle at build time. Add a `staging` entry under `serve` with `"buildTarget": "my-app:build:staging"` if you want `ng serve -c staging`. Remember that a configuration you select explicitly replaces the default one, so production-only settings such as budgets and hashing must be repeated or layered with `-c production,staging`.

code

json · 29 lines
json
{
  "projects": {
    "my-app": {
      "architect": {
        "build": {
          "builder": "@angular/build:application",
          "defaultConfiguration": "production",
          "configurations": {
            "staging": {
              "outputHashing": "all",
              "fileReplacements": [
                {
                  "replace": "src/environments/environment.ts",
                  "with": "src/environments/environment.staging.ts"
                }
              ]
            }
          }
        },
        "serve": {
          "builder": "@angular/build:dev-server",
          "configurations": {
            "staging": { "buildTarget": "my-app:build:staging" }
          }
        }
      }
    }
  }
}

go deeper

for a junior

Recall that environment files hold per-environment values, that code imports environment.ts, and that ng build -c staging selects the staging configuration.

for a middle

Explain how fileReplacements swaps files at compile time, what ng generate environments creates, and why serve needs its own staging entry with a buildTarget.

for a senior

Catch the configuration-selection trap, where staging loses production's budgets and hashing, and decide between repeating options and layering configurations.

for a principal

Weigh build-per-environment files against runtime configuration when one artifact must move through several stages.

## The goal A team needs a third build, **staging**, that talks to the staging API and enables a debug banner, while `production` and `development` keep their own values. The Angular CLI solves this with two pieces of `angular.json`: a **named configuration** and **`fileReplacements`**. ## Step 1: create the environment files ```bash ng generate environments ``` The schematic, which only works on application projects with a `build` target: - creates `src/environments/environment.ts`, the file used by the **default** configuration (normally `production`); - for every other build configuration, such as `development`, creates `environment.<name>.ts` and adds a `fileReplacements` entry to that configuration; - skips files and entries that already exist, so it is safe to run again after adding a configuration. The generated files contain only `export const environment = {};`; you add the properties. ```ts // src/environments/environment.ts (production values) export const environment = { apiUrl: 'https://api.example.com', showDebugBanner: false }; // src/environments/environment.staging.ts export const environment = { apiUrl: 'https://staging-api.example.com', showDebugBanner: true }; ``` ## Step 2: add the staging configuration ```json "build": { "builder": "@angular/build:application", "defaultConfiguration": "production", "configurations": { "production": { "budgets": [ ... ], "outputHashing": "all" }, "development": { ... }, "staging": { "outputHashing": "all", "fileReplacements": [ { "replace": "src/environments/environment.ts", "with": "src/environments/environment.staging.ts" } ] } } } ``` Then build it with `ng build --configuration staging`. ## Step 3: let serve use it too `ng serve -c staging` looks up a `staging` configuration on the **serve** target, not the build target. Add one: ```json "serve": { "configurations": { "staging": { "buildTarget": "my-app:build:staging" } } } ``` Without it the CLI stops with an error saying that configuration `staging` is not set for target `serve`. ## What fileReplacements actually does - It maps one source path to another **inside the compilation**. When the compiler loads `src/environments/environment.ts`, it reads `environment.staging.ts` instead. - Paths are relative to the **workspace root**. If the `with` file does not exist, the build fails immediately. - In the application builder, both paths must be script or JSON files (`.ts`, `.js`, `.mjs`, `.tsx`, `.json` and similar); stylesheets and templates cannot be swapped this way. - Code must import the **original** path. A component that imports `environment.staging.ts` directly gets staging values in every build. - The replacement happens at **build time**. The values end up in the JavaScript bundle, readable by anyone who loads the page, so the files must never hold secrets. ## The trap: selected configurations do not stack on the default `defaultConfiguration` is used **only when no configuration is named**. `ng build -c staging` therefore applies `options` plus `staging`, and **not** `production`. Anything production added, such as `budgets` and `outputHashing: "all"`, is missing unless staging repeats it or you layer both: ```bash ng build -c production,staging ``` Configurations listed with commas are applied left to right, and a later configuration replaces a whole option set by an earlier one, so the staging `fileReplacements` array replaces production's rather than merging with it. ## Common mistakes - **Putting `fileReplacements` under `options`** instead of under `configurations.staging`: every configuration, production included, then compiles the staging file. - **Importing the replacement file directly**, which bypasses the swap for that import. - **Forgetting the serve entry**, then assuming `ng serve -c staging` is broken. - **Diverging shapes**: if `environment.staging.ts` lacks a property that `environment.ts` has, the problem only surfaces when the staging configuration is built. Keep every environment file exporting the same shape, for example by typing them against a shared interface. - **Treating the files as runtime configuration**: changing `environment.staging.ts` after the build changes nothing until the next build. ## Summary of the pieces | Piece | Where | Purpose | |---|---|---| | `environment.ts` | `src/environments/` | default values, imported by the code | | `environment.staging.ts` | `src/environments/` | staging values | | `configurations.staging.fileReplacements` | build target | swaps the file at compile time | | `configurations.staging.buildTarget` | serve target | lets `ng serve -c staging` compile it |

  • What happens if a component imports environment.staging.ts directly?
    It bypasses the replacement: `fileReplacements` only intercepts loads of the `replace` path, so that component gets staging values in every build, production included. All code must import the original `environment.ts` and let the configuration decide what is compiled.
  • Can one production bundle be promoted to staging if the API URL comes from fileReplacements?
    No. The replacement happens at build time, so each configuration produces a different bundle with its values inlined. Promoting one artifact through stages needs configuration loaded at runtime instead, which is a different design from environment files.
  • Is it safe to put an API key in environment.staging.ts?
    No. Environment files are compiled into the client bundle and are readable by anyone who loads the page. Secrets belong on a server, behind a proxy or a backend endpoint, never in `src/environments/`.

fileReplacements works like a stage manager swapping the prop on the table before the curtain rises: the actors always reach for the same spot, and which object they pick up depends on which show is being staged tonight.

saying these in an interview costs you the question

  • Components should import environment.staging.ts directly in staging code paths.
  • fileReplacements swaps files at runtime depending on the host name.
  • ng build -c staging also applies the production configuration's settings.
  • Adding a staging build configuration makes ng serve -c staging work automatically.
  • Environment files are a safe place for API keys because they are not public.