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

Google-play-scraper encounters an unhandled promise rejection

I'm trying to use the google-play-scraper library with Node.js, but I keep encountering an error when passing a variable as the 'appId'. How can I resolve this issue? Example that works: var gplay = require('google-play-scraper') ...

What is the best way to represent the concept of "having at least one existing property and not having any additional properties" using a mapped type?

Apologies for the slightly lengthy title. Consider the following type: type A = { foo: string; bar: number; baz: boolean; } I want to define a new "partial" type B type B = Partial<A> where B must have at least one property of A and on ...

I'm experiencing some strange symbols on my page that look like ''. It appears to be a problem occurring between the servlet and the Javascript. How can I resolve this issue?

After retrieving a CSV file from my servlet, I noticed that characters like 'é', 'á' or 'õ' are not displaying properly on my page. Strangely, when I access the servlet directly via browser, everything appears fine. I atte ...

Accessing a key from an AJAX JSON response and applying it within a .each() loop in jQuery

I'm struggling with a seemingly simple task that I just can't seem to get right. My goal is to convert multiple text inputs into select fields using AJAX and jQuery. Everything works smoothly, except when trying to make the $.each function dynam ...

The storage does not reflect updates when using redux-persist with next-redux-wrapper in a Typescript project

I'm currently working on integrating redux-persist with next.js using next-redux-wrapper. However, I'm facing an issue where the storage is not updating and the state is lost during page refresh. Below is my store.ts file: import { createStore, ...

The model in the Schema has not been registered, unlike other models from the same source that have been successfully registered

When populating a node express route with information from Schemas, I encountered an error that puzzles me. Even though I am referencing three different fields in the same Schema, I am only facing this error for one of those fields. The specific error mes ...

Creating pathways in AJAX with Rails

Issue at hand: undefined variable article_id. The objective: Setting up the correct route for AJAX and Rails. What I require: The format articles/1/comments/2. Main goal: To specifically load comment through AJAX, not article. In the current AJAX scrip ...

Automatic capitalization feature for input fields implemented with AngularJS

I have integrated a directive into my input field to validate a license key against a server-side API. While this functionality works well, I also want the license key to automatically add hyphens and appear in capital letters. For example, when a user in ...

Issues are arising with the .mouseover functionality within this particular code snippet

Learning Javascript has been a challenge for me so far. I tried following a tutorial, but the result I got wasn't what I expected based on the video. I'm wondering why that is and how I can fix it. I'm aiming to make a box appear suddenly w ...

Attempting to dynamically insert a new row into a table using jQuery, with the added condition of limiting the total number of rows to four

I'm facing an issue with a table that has no rows. I want to dynamically add rows by clicking an anchor tag, with a limit of 4 rows. When the limit is reached, I need to display an alert message saying "only 4 rows allowed." However, currently, it kee ...

Encountering build error ts2307 while using Angular 6 with npm: Module "ngx-loading-mask" not found

After exhausting all my search efforts on Google, I am stuck at a roadblock while upgrading from version 5.x to 6.x. My npm install runs smoothly with just 2 WARNS that are not causing any problems. But there are two strange issues that I can't seem ...

Exploring URL Parameters in Angular Unit Testing

My goal is to execute a test to check for the presence of a specific string in URL parameters. Inside my TypeScript file, I have defined the following method: checkURLParams() { if (this.route.parent) { this.route.parent.params.subscribe((params) ...

What is the recommended approach in Angular for unassigned @Input() variables when using TypeScript strict mode?

Issue at Hand After upgrading my Angular version from 10 to 13, I encountered a problem with the new TypeScript strict compiler mode. The upgrade required me to assign values to all declared variables upon initialization, causing issues with properties de ...

What is the meaning of boolean true in a Firestore query using TypeScript?

Currently, I am facing an issue with querying Firestore in Angular 8 using AngularFire. While querying a string like module_version works perfectly fine as shown in the code snippet below, the problem arises when attempting to query a boolean field in Fire ...

NextJS build problem causing all content to become static

As a newcomer to NextJS with prior experience in basic React apps, I recently attempted to deploy a CRUD NextJS app (utilizing prisma and mongoDB). Everything runs smoothly with npm run dev, but issues arise when transitioning to npm run build followed by ...

How to Retrieve the Access Token from Instagram using Angular 2/4/5 API

I have integrated Instagram authentication into my Angular 2 project using the following steps: Begin by registering an Instagram Client and adding a sandbox user (as a prerequisite) Within signup.html, include the following code snippet: <button t ...

I was caught off guard by the unusual way an event was used when I passed another parameter alongside it

One interesting thing I have is an event onClick that is defined in one place: <Button onClick={onClickAddTopics(e,dataid)} variant="fab" mini color="primary" aria-label="Add" className={classes.button}> <AddIcon /> & ...

Error in AWS Cloud Development Kit: Cannot access properties of undefined while trying to read 'Parameters'

I am currently utilizing aws cdk 2.132.1 to implement a basic Lambda application. Within my project, there is one stack named AllStack.ts which acts as the parent stack for all other stacks (DynamoDB, SNS, SQS, StepFunction, etc.), here is an overview: im ...

Leverage jQuery to Retrieve Text and Modify

My Content Management System automatically generates a time stamp for when a page was last updated. However, the format it provides is not ideal for my needs. I would like the date to be displayed in the US Standard way - May 24, 2013, without including th ...

Obtain a collection of lists stored in Firebase

As I work on creating an Ionic app with a Firebase database, my data structure in Firebase looks like the image below. I am utilizing angularfire2 to retrieve this data: https://i.stack.imgur.com/5akQf.png While I have successfully obtained the list of pa ...