Angular 15 standalone HTTPClient provider: Another update
Tags: angular, webdev, tutorial, typescript
Published: 2022-11-29 12:00:00

Another update, or maybe it was there but buried deep in documentation, to allow us to use [HttpClient Module-less in Angular](https://angular.io/api/common/http/provideHttpClient). The new function provided is `provideHttpClient`. So let's revisit our [StackBlitz](https://stackblitz.com/edit/angular-standalone-app-full) project and apply this new feature.

Adding HTTP Client with interceptors
------------------------------------

With modules, it looks like this. I added in a couple of interceptors to the project as usual (class-based).

```ts
// Http providers array fed to root application, standalone or not
export const HttpProviders = [
  importProvidersFrom(HttpClientModule),
  // A couple of interceptors
  {
    provide: HTTP_INTERCEPTORS,
    useClass: LocalInterceptor,
    multi: true
  },
  {
    provide: HTTP_INTERCEPTORS,
    useClass: GarageInterceptor,
    multi: true
  }
];
```

To make the transition simpler, Angular provides a function that allows these class-based interceptors as are, using `withInterceptorsFromDi`

```ts
// change into standalone module-less HTTPClient
export const HttpProviders = [
  provideHttpClient(
    // do this, to keep using your class-based interceptors.
    withInterceptorsFromDi()
  ),
  // A couple of interceptors
  {
    provide: HTTP_INTERCEPTORS,
    useClass: LocalInterceptor,
    multi: true
  },
  {
    provide: HTTP_INTERCEPTORS,
    useClass: GarageInterceptor,
    multi: true
  }
];
```

The `HttpProviders` is supplied to the root providers in `main.ts`, or `AppModule` if used.

```ts
// main.ts
bootstrapApplication(AppComponent, {
  providers: [
    // ...
    // lets add http providers
    ...HttpProviders,
  ],
});
```

Running this in StackBlitz, I can see the interceptors worked well. Now lets move to Angular's suggestion: `withInterceptors`

> Prefer `withInterceptors` and functional interceptors instead, as support for DI-provided interceptors may be phased out in a later release---Per documentation.

Using functional interceptor
----------------------------

The other method is to change the interceptor classes into [functions](https://angular.io/api/common/http/HttpInterceptorFn) of type: `HttpInterceptorFn`

```ts
// pass the interceptor functions to the HTTP providers:
export const HttpProviders = [
  provideHttpClient(
    // do this, to change to functional interceptors
    withInterceptors([
       LocalInterceptorFn,
       GarageInterceptorFn
    ])
  )
];
```

The functions look like this:

```ts
// local interceptor function
// notice it exports a function of type HttpInterceptorFn
// also take note of the signature, it uses HttpHandlerFn new function
export const LocalInterceptorFn: HttpInterceptorFn = (
  req: HttpRequest<any>,
  next: HttpHandlerFn
) => {
  if (req.url.indexOf('localdata') < 0) {
    // notice the direct use of `next` without `.handle()`
    return next(req);
  }
  // do something with url, then handle
  let url = req.url;
  console.log('url from local interceptor', url);

  const adjustedReq = req.clone({ url: url });

  return next(adjustedReq);
}
```

Injecting a service
-------------------

Do you inject a service like the Loader service? You can use `inject` function to continue to use it. The [inject function](https://angular.io/api/core/inject), not the [Inject decorator](https://angular.io/api/core/Inject)).

```ts
// http interceptor function with an injected service
export const GarageInterceptorFn: HttpInterceptorFn = (
  req: HttpRequest<any>,
  next: HttpHandlerFn
) => {
  // do you inject a client service? it goes like this
  // import { inject } from '@angular/core';
  const loaderService = inject(LoaderService);
  loaderService.show();

  // ...
  return next(adjustedReq).pipe(
    finalize(() => {
        loaderService.hide();
     }),
	);
};
```

Injecting a server token
------------------------

Do you inject a token that is provided by SSR? Even though [standalone has not reached ExpressEngine](https://github.com/angular/universal/issues/2799), but if you have the root `AppModule` and the root `AppComponent` non standalone, you can still use `provideHttpClient`. To inject a server provided string (specifically, the `serverUrl`), you can use `inject` function like this:

```ts
// local interceptor with serverUrl provided by ngExpressEngine for SSR
export const LocalInterceptorFn: HttpInterceptorFn = (
  req: HttpRequest<any>,
  next: HttpHandlerFn
) => {
  // ...
  // cast to 'any' if you do not want to wrestle with Angular and Typescript
  const serverUrl = inject(<any>'serverUrl', {optional: true});
}
```

Cast the injection token to `any` if you do not wish to wrestle with Angular and TypeScript. The alternative is ugly:

`<ProviderToken<*unknown*>><*unknown*>'serverUrl'`

Also pass the `optional: true` flag to replace the `@Optional` decorator in the previous DI method.

Building locally for SSR, the `serverUrl` was passed in as expected.

Thank you for diving in with me today, did you have to hold your breath for more than 4 minutes? Hope not.

### RESOURCES

-   [The Refurbished HttpClient in Angular 15](https://www.angulararchitects.io/aktuelles/the-refurbished-httpclient-in-angular-15-standalone-apis-and-functional-interceptors/)
-   [Inject function in Angular](https://angular.io/api/core/inject)
-   [provideHttpClient Angular Documentation](https://angular.io/api/common/http/provideHttpClient)
-   [StackBlitz project](https://stackblitz.com/edit/angular-standalone-app-full)

{% embed https://garage.sekrab.com/posts/angular-standalone-httpclient-provider %}