Angular Forkable HTTP Client
ngx-forkable-http-client is an Angular library that extends the framework's standard `HttpClient` to introduce the concept of 'forking'. This enables developers to create new, isolated instances of `HttpClient` that can have their own specific `HttpInterceptor`s, rather than all interceptors being applied globally. This is particularly useful for applications interacting with multiple external APIs, each requiring different authentication, logging, or error handling mechanisms. The library, currently at version 4.0.0, maintains a strict compatibility matrix with Angular major versions, releasing new major versions as Angular itself evolves. Its primary differentiator is solving the common Angular challenge of managing non-global HTTP interceptors and establishing a hierarchical structure for HTTP client configurations.
Common errors
-
Error: NG0201: No provider for InjectionToken MY_REST_API_HTTP_CLIENT!
cause The `InjectionToken` for your forked `HttpClient` is not correctly provided in the Angular dependency injection system.fixEnsure your `InjectionToken` specifies `providedIn: 'root'` or that it is explicitly added to the `providers` array of an `@NgModule` where it needs to be available. Example: `new InjectionToken<ForkableHttpClient>(..., { providedIn: 'root', factory: ... });` -
TypeError: httpClient.fork is not a function
cause You are attempting to call `fork()` on Angular's native `HttpClient` instead of the extended `ForkableHttpClient` provided by this library.fixEnsure you are injecting `ForkableHttpClient` from `ngx-forkable-http-client` and not `HttpClient` from `@angular/common/http` when you intend to use the forking functionality. Update your import and injection site. -
Argument of type 'typeof MyInterceptor' is not assignable to parameter of type 'HttpInterceptor'.
cause The class provided to `httpClient().with()` or `ForkableHttpClient.fork()` does not correctly implement the `HttpInterceptor` interface, or it's not marked as `@Injectable()`.fixVerify that your interceptor class has an `intercept` method with the correct signature (`intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>>`) and is decorated with `@Injectable()`.
Warnings
- breaking Major versions of `ngx-forkable-http-client` are tightly coupled to specific Angular versions. Version 4.x.x requires Angular >=13.0.0. Using an incompatible library version will lead to build errors or runtime failures.
- gotcha When defining a forked HTTP client, ensure you use the `httpClient().with(...)` factory pattern. Simply injecting `ForkableHttpClient` without this factory setup will not apply the intended non-global interceptors to that specific instance.
- gotcha Misunderstanding the 'fork' concept can lead to confusion with `rxjs.forkJoin`. This library's 'fork' refers to creating a new `HttpClient` instance that inherits from a parent but can have additional interceptors. It does not relate to concurrent Observable execution.
Install
-
npm install ngx-forkable-http-client -
yarn add ngx-forkable-http-client -
pnpm add ngx-forkable-http-client
Imports
- ForkableHttpClient
import { HttpClient as ForkableHttpClient } from '@angular/common/http';import { ForkableHttpClient } from 'ngx-forkable-http-client'; - httpClient
import { httpClient } from '@angular/common/http';import { httpClient } from 'ngx-forkable-http-client'; - InjectionToken
import { InjectionToken } from '@angular/core';
Quickstart
import { InjectionToken, Injectable } from '@angular/core';
import { HttpInterceptor, HttpRequest, HttpHandler, HttpEvent } from '@angular/common/http';
import { Observable } from 'rxjs';
import { ForkableHttpClient, httpClient } from 'ngx-forkable-http-client';
// Dummy Interceptors for demonstration
@Injectable()
export class MyAuthenticationHttpInterceptor implements HttpInterceptor {
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
console.log('Auth Interceptor: ', req.url);
const authReq = req.clone({ setHeaders: { Authorization: `Bearer ${process.env.AUTH_TOKEN ?? 'my-secret-token'}` } });
return next.handle(authReq);
}
}
@Injectable()
export class LoggingHttpInterceptor implements HttpInterceptor {
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
console.log('Logging Interceptor: ', req.method, req.url);
return next.handle(req);
}
}
@Injectable()
export class ErrorHandlerHttpInterceptor implements HttpInterceptor {
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
console.log('Error Handler Interceptor: ', req.url);
return next.handle(req);
}
}
@Injectable()
export class AnotherHttpInterceptor implements HttpInterceptor {
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
console.log('Another Interceptor: ', req.url);
return next.handle(req);
}
}
export const MY_REST_API_HTTP_CLIENT =
new InjectionToken<ForkableHttpClient>('MY_REST_API_HTTP_CLIENT', {
providedIn: 'root',
factory: () => httpClient().with(MyAuthenticationHttpInterceptor, LoggingHttpInterceptor)
});
export const EXTERNAL_API_X_HTTP_CLIENT =
new InjectionToken<ForkableHttpClient>('EXTERNAL_API_X_HTTP_CLIENT', {
providedIn: 'root',
factory: () => httpClient().with(ErrorHandlerHttpInterceptor)
});
export const EXTERNAL_API_Y_HTTP_CLIENT =
new InjectionToken<ForkableHttpClient>('EXTERNAL_API_Y_HTTP_CLIENT', {
providedIn: 'root',
factory: () => httpClient().with(AnotherHttpInterceptor)
});
// Example of how to use in a component or service:
// @Component({...})
// export class MyService {
// constructor(@Inject(MY_REST_API_HTTP_CLIENT) private myApiClient: ForkableHttpClient) {
// this.myApiClient.get('/data').subscribe(res => console.log(res));
// }
// }