Angular Tips | Environmental Providers using CLI Builders
Tags: angular, cli, builder
Published: 2023-02-27 20:38:25

---
title: Angular Tips | Environmental Providers using CLI Builders
published: true
description: 
tags: angular, cli, builder
series: NgTales
# cover_image: https://direct_url_to_image.jpg
# Use a ratio of 100:42 for best results.
# published_at: 2023-02-27 08:47 +0000
---

![Providers that are injected depending on the configuration.](https://cdn-images-1.medium.com/max/2000/1*f9fzWHYeFS5R7oPNkSqLag.png)

Have you ever thought about configuring **different Angular development environments to consume resources or services depending on the configuration? Generate builds of the same application that consume resources from different sources?** Then read on!

In some Web application development scenarios that work with APIs, we may not have access to them at will. In these cases, solutions such as **proxies, fake/mock servers**… are often used.

All of them are good solutions, but… Can we take advantage of **Angular Dependency Injection and the CLI Builders to create and provide services depending on the environment**? Let’s see it :)
>  First of all, if you are not familiar with Angular configurations, I advise you to take a look at the [official documentation section](https://angular.io/guide/workspace-config#build-target).

Our goal is to achieve the following behavior:

```bash
# Should init dev-server with HTTP Providers*
ng serve

# Should init dev-server with Faker Providers*
ng serve --configuration faker
```

# Setup Environment

```bash
# Create a new Angular Project
ng new env-providers

# Install needed dependences
npm install --save-dev @angular-builders/custom-webpack
npm install --save faker
```

# Modeling Providers

We need to get the following structure:

* **FooInterface**: defines the interface to be implemented by the different services (Http & Faker)

> foo-service/foo-service.interface.ts
```ts
import { Observable } from 'rxjs';
     
export interface IFooService {
  retrieve(id: number): Observable<Foo>;
}
```

* **FooService (HTTP impl.)**: concrete implementation of the service with access to data by **HTTP** protocol.

> foo-service/foo.servicice.ts
```ts
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';
import { IFooService } from './foo-service.interface';

import { HttpClient } from '@angular/common/http';

@Injectable({
  providedIn: 'root'
})
export class FooService implements IFooService {

  constructor(
    private http: HttpClient
  ) {}

  retrieve(id: number): Observable<Foo> {
    return this.http.get(`${environment.API_URL}/foo`);
  }
}
```

* **FooService (Faker impl.)**: concrete implementation of the service of objects generated by **Faker**.

> foo-service/foo*.servicice.faker.ts

```ts
import { Injectable } from '@angular/core';
import { Observable, of } from 'rxjs';
import { IFooService } from './foo-service.interface';

import * as faker from 'faker';

@Injectable({
  providedIn: 'root'
})
export class FooService implements IFooService {

  retrieve(id: number): Observable<Foo> {
    return of(new Foo({
      id: id,
      name: faker.name
    }));
  }
}
```

Now we have our providers ready to use, so … Let’s do it!

> app.component.ts
```ts
import { Component, OnInit } from '@angular/core';
import { FooService } from './foo-service/foo.service';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {

  public foo$: Observable<Foo>;

  constructor(
    private *fooService*: FooService
  ) {}

  ngOnInit() {
    this.foo$ = this.fooService.retrieve(1);
  }
}
```

Note that the service is being imported from /foo.service, that is, **the service that will be used by default when running ng serve without additional parameters.**

# Configuring & Hacking CLI Builders

Now that we have our services created and are being consumed from a component of our application, we will configure the project so that the Angular CLI works as described at the beginning of the article.

## Artifact & builder configuration

We must modify the angular.json file with a configuration similar to the one I show below:

> angular.json
```json
"projects": {
  . . .
  "architect": {
    /* <build> artifact config */
    "build": {
      /* Custom artifact builder */
      "builder": "@angular-builders/custom-webpack:browser",
        "options": {
          /* Our custom builder adjustment */
          "customWebpackConfig": {
            "path": "./providers.config.ts"
          },
            . . .
          },
          "configurations": {
            "production": {
              . . .
            },
            /* Our build configuration */
            "faker": {
              "tsConfig": "tsconfig.app.faker.json"
            }
         }
      }    
    }
  }
}
```

In this file, we perform 3 important changes:

* We changed the default Angular builder to the one we installed from **@angular-builders/custom-webpack.**

* With the **customWebpackConfig** property we indicate the file that will perform the necessary modifications to inject some or other providers. We will build later.

* We create a new configuration called faker. This will perform the TypeScript transpilation with the configuration defined in tsconfig.app.faker.json .

The new tsconfig file:

> tsconfig.app.faker.json
```json
{
  "extends": "./tsconfig.app.json",
  "include": [
    "src//*.d.ts",
    "src/**/*.service.faker.ts"
  ]
}
```

What we are doing with this configuration is explicitly telling Typescript to transpile the files ending with *.service.faker.ts. This is because **if imports of the files are not found, the TypeScript compiler will not compile them unless explicitly specified** (includeoption).

## Extending Builder behavior

Finally, we will extend the functionality of the CLI Builder with the following configuration.

> providers.config.ts
```ts
import * as webpack from 'webpack';

const PROVIDER_FILE_CASING = '.service';

// Retrieve '--configuration' parameter*
const configurationIndex =
  process.argv.indexOf('--configuration') > -1
    ? process.argv.indexOf('--configuration') + 1
    : 0;
const configuration = configurationIndex
    ? `${process.argv[configurationIndex]}`
    : '';

export default (*config*: webpack.Configuration) => {
  config.plugins.push(
  /*
   * Using webpack plugin to replacement:
   *   We are searching all '.service' occurrences
   *   & replace it to `.service.<configuration>`
   */
     new webpack.NormalModuleReplacementPlugin(
       new RegExp(`(.*)${PROVIDER_FILE_CASING}(\.*)`),
         resource => {
           resource.request = resource.request.replace(
             new RegExp(`${PROVIDER_FILE_CASING}`),
             `${PROVIDER_FILE_CASING}.${configuration}`
           );
         }
      )
   );
   // New config is returned to Builder
  return config;
};
```

We are creating a *replacement rule* that replaces:

* .service => *.service.faker

*That is, replace HTTP providers with Faker providers.*

And that’s it! Now, when we execute the ng serve command, the application will be built on the development server with the **HTTP providers**, while if we run ng serve --configuration faker, it will do so with the **Faker providers**.

# Applicability

We can use this approach if…

* We need to **configure different development environments** that access to data from different sources or comunication protocols.

* We need to **generate different builds** of our application based on the *Data Access Layer*.

* We want to **reduce test execution time** due to *server latency* in test environments or **we cannot access it**.