DEV Community

Cover image for Angular 15 standalone HTTPClient provider: Another update
Ayyash
Ayyash

Posted on • Originally published at garage.sekrab.com

Angular 15 standalone HTTPClient provider: Another update

Another update, or maybe it was there but buried deep in documentation, to allow us to use HttpClient Module-less in Angular. The new function provided is provideHttpClient. So let's revisit our StackBlitz 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).

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

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

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

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

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

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 of type: HttpInterceptorFn

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

The functions look like this:

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

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, not the Inject decorator).

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

Injecting a server token

Do you inject a token that is provided by SSR? Even though standalone has not reached ExpressEngine, 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:

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

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

Top comments (0)