Issues detected with the functionality of Angular HttpInterceptor in conjunction with forkJoin

I have a Service that retrieves a token using Observable and an HttpInterceptor to inject the token into every http request. It works seamlessly with a single request, but when using forkJoin, no response is received.

Here is the code for the interceptor:

import { HttpErrorResponse, HttpEvent, HttpHandler, HttpInterceptor, HttpRequest } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Observable, } from 'rxjs';
import { catchError, map, switchMap } from 'rxjs/operators';
import { AppService } from './app.service';
@Injectable()
export class AuthInterceptor implements HttpInterceptor {
  constructor(
    private service: AppService
  ) { }

  intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    return this.service.token$.pipe(
      map((token: string) => {
        if (token) {
          const headers = req.headers.set('Authorization', `Bearer ${token}`);
          console.log(`Request for url ${req.url}`);
          req = req.clone({
            headers: headers
          });
        }
        return req;
      }
      ),
      switchMap(newReq => next.handle(newReq))
    )
  }
}

Below are examples of two simple requests:

  getUsers() {
    return this.http.get<any[]>(`https://jsonplaceholder.typicode.com/users`);
  }

  getPosts() {
    return this.http.get<any[]>(`https://jsonplaceholder.typicode.com/posts`);
  }

In the component:

// Single One will work
 this.appService.getPosts().subscribe(res => console.warn(res));

// Will not work
    forkJoin([this.appService.getPosts(), this.appService.getUsers()])
      .subscribe(([posts, users]) => {
        console.log(posts, users);
      });

The error can be replicated in the following example: https://stackblitz.com/edit/angular-kpxvej

Adding take(1) in the interceptor resolves the issue, but it does not match the desired behavior as it would only use the initial token value.

If the token is just a string, the following interceptor code snippet works:

  intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    const token = this.service.getToken();
    const headers = req.headers.set('Authorization', `Bearer ${token}`);
    console.log(`Request for url ${req.url}`);
    req = req.clone({
      headers: headers
    });
    return next.handle(req);
  }

Answer №1

Based on what I understand, if you utilize the token as an Observable and switchMap in the interceptor, you might find that one request can cancel out another.

In your specific scenario, it would go like this: (getUsers triggers -> token added in the interceptor & getPosts triggered -> token added in the interceptor) -> switchMap (cancels the previous intercepted request) -> only 1 request actually goes through.

With forkJoin, both observables need to complete before firing, so in your case, one gets a server response while the other remains quiet.

You could opt for mergeMap instead of switchMap (since it won't cancel the request), but the optimal solution would be to use switchMap before calling the services function.

Furthermore, it might be more practical to treat the token as a string rather than an observable since it usually doesn't change during a session, and you can use set-get with localStorage to ensure it's always updated.

I hope this explanation proves helpful.

Answer №2

Although it may be a bit late to add to Ngoc Nam Nguyen's helpful answer, I found another solution that worked for me. I switched to using mergeMap inside the interceptor and implemented take one to ensure that any token changes would not affect the interceptor later on. Here is how my updated interceptor looks like:

intercept(req: HttpRequest<unknown>, next: HttpHandler): Observable<HttpEvent<unknown>> {
return this.authFacade.token$.pipe(
  take(1),
  mergeMap((token: string) => {
    if (token && req.method === 'POST') {
      const request = req.clone({
        setHeaders: { Authorization: `Bearer ${token}` },
      });
      return next.handle(request);
    }
    return next.handle(req);
  })
);
}

This way, I was able to maintain the token as an observable, which I believe is a more efficient approach.

Answer №3

Experiment with this particular Reactive setup by integrating the forkjoin as an observable.

service.ts

fetchUsers(): Observable<any[]> {

        return this.http.get(`https://jsonplaceholder.typicode.com/users`)
        .map(res => { 
            return res;
          });
    }

getPostsList(): Observable<any[]> {            
            return this.http.get(`https://jsonplaceholder.typicode.com/posts`);
            .map(res => { 
                return res;
              });
        }

obtainForkJoinData(): Observable<any>{
        return forkJoin(
            this.fetchUsers(),
            this.getPostsList()           
        );
    }

Component.ts

this.dataService.obtainForkJoinData().subscribe(([users,posts]) =>{

console.log(users);
console.log(posts);

} );

Answer №4

By transforming the token from Observable to a regular string, I successfully resolved the issue at hand.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Utilize Chrome storage instead of localstorage to generate Parse sessions

I'm currently developing a Chrome Extension that relies on Parse User sessions. Because localstorage is limited to specific domains, I am looking to utilize chrome.storage so the data can be accessed across any site. The existing Parse Javascript SDK ...

Exploring methods for interacting with and controlling structural directives in e2e testing

Background: My goal is to permutation all potential configurations of an Angular2 screen for a specified route and capture screenshots using Protractor from the following link: http://www.protractortest.org/#/debugging. Problem: I am struggling to figure ...

Retrieve the attribute from a TypeScript union data type

Here is the structure that I am working with: export interface VendorState extends PaginationViewModel { vendors: CategoryVendorCommand[] | CategoryVendorCommand; } This is my model: export interface CategoryVendorCommand { id: string; name: str ...

The React component does not trigger a re-render

Using React Redux, I am able to pass values successfully. However, the component I intend to render only does so once. Interestingly, when I save my code in VSCode, it renders again and the data displays on the page as expected. return ( <div classN ...

How can you typically verify the type of an object variable in TypeScript?

How can I verify if the obj variable contains all the properties defined in the Person interface? interface Person { name: string; age: number; } const jsonObj = `{ name: "John Doe", age: 30, }`; const obj: Person = JSON.parse(jsonObj); ...

Implementing a line break within a JavaScript function specifically designed for formatting images

As I am not a javascript developer, the code I have been given for debugging is somewhat challenging. The issue at hand involves an iOS module where a .js CSS file has been set up and images are loading improperly, resulting in the need for horizontal scro ...

How can I find the URL of a webpage that is not showing up in the search bar? Utilize Google Instant

I'm currently working on an extension and I've encountered a challenge... I'm trying to figure out how to extract the URLs from a Google instant search page. The browser's URL bar doesn't seem to update instantly, so I'm unsur ...

Interacting with Vue3 List Items by Manipulating the HTML DOM

I am currently using Vue 3 and I have a requirement to manipulate a specific list item when a button is clicked. Below is the HTML code snippet: <socialDiv v-for="(follower, i) in followerList" :key="follower.id" :ref="el => ...

Getting latitude and longitude from Google Maps in a React Native appHere are the steps to

Looking to retrieve the latitude and longitude from Google using React Native. As a newcomer to React Native, I have been unable to find any resources to address this issue. Any assistance would be greatly appreciated. Thanks! Thanks ...

Facing an ESIDIR error in NextJs, despite the fact that the code was sourced from the official documentation

For an upcoming interview, I decided to dive into learning Next.js by following the tutorial provided on Next.js official website. Everything was going smoothly until I reached this particular section that focused on implementing getStaticProps. Followin ...

Ensure that Angular resolver holds off until all images are loaded

Is there a way to make the resolver wait for images from the API before displaying the page in Angular? Currently, it displays the page first and then attempts to retrieve the post images. @Injectable() export class DataResolverService implements Resolv ...

Incorporating dynamic elements without sacrificing the original static page

I have a compilation of video titles. Each title links to a page featuring the specific video along with additional details such as player information, description, and discussion. <div id="video-list"> <a href="/Video/title-1">Title 1</a&g ...

Access uninitialized properties in Typescript post-compilation

I am currently in the process of creating a wrapper for socket.io. Coming from a strong object-oriented background, I aim to incorporate the idea of Models into my framework/wrapper. For those familiar with socket.io, you may know that data associated wit ...

Minimizing the gap between icon and label text

I have a React form that I need help with. The issue is that I want to reduce the space between the list icon and the label. Here is the CSS I am using: .form__container { display: flex; flex-wrap: wrap; } .form__container input { color: rgb(115, 0, ...

Contrasting results when logging an element in Chrome versus IE

Running the script below in Internet Explorer gives the expected output for console.log(target_el): <div class="hidden"></div> However, when run in Chrome, the output changes to: <div class="visible"></div> To add a humorous twi ...

Gather information from a line of Javascript using Python's scraping capabilities

Is there a way to extract JSON data from a Javascript line using Python? AH4RSearch.listingsJSON = $.parseJSON('{"properties":[{"Price":3695,"PriceFormatted":"3,695","Street":"9251 E Bajada Road&q ...

What could be causing the TypeScript class property to be undefined?

Below is the code snippet I am working with: class FeedbackController { public homePage(req, res){ this.test(); res.send('Welcome to feedback service'); } private test(){ console.log('test called'); } } export de ...

Does ng-include fetch the included HTML files individually or merge them into a single HTML file before serving?

Is there a performance impact when using the ng-include Angular directive, in terms of having included HTML files downloaded as separate entities to the user's browsers? I am utilizing a CDN like AWS CloudFront instead of a node server to serve the H ...

Update or overwhelm a node_module file reference

Let's say I have an installed node_module called magicalModule, which was installed using the command npm i magicalModule. To use it in my code, I import it like this: const magic = require('magicalModule'). Now, is there a way for me to o ...

Issues with Rxjs pipe and Angular's Http.get() functionality are causing complications

Working with an Angular 4 Component that interacts with a Service to fetch data is a common scenario. Once the data is retrieved, it often needs to be transformed and filtered before being utilized. The prevailing method for this task is through the use of ...