Cancel repeated requests in Angular with interceptors

Shrihari Mohan - Apr 16 '23 - - Dev Community

By using interceptors, you can intercept and modify HTTP requests and responses globally across your entire application.

In this we're going to create a component , service and interceptor


In src/app/cancel-repeated-apis/cancel-repeated-apis.html



<div class="h-screen gap-4 flex items-center justify-center bg-black">
  <button class="p-3 bg-cyan-400  rounded-lg" (click)="handleClick(1)">
    API 1
  </button>
  <button class="p-3 bg-red-400 rounded-lg" (click)="handleClick(2)">
    API 2
  </button>
</div>


Enter fullscreen mode Exit fullscreen mode

we're using tailwind along with it. Nothing serious , just 2 buttons with different colors.

In src/app/cancel-repeated-apis/cancel-repeated-apis.ts



import { Component } from '@angular/core';
import { ApiService } from '../services/api.service';

@Component({
  selector: 'app-cancel-repeated-apis',
  templateUrl: './cancel-repeated-apis.component.html',
  styleUrls: ['./cancel-repeated-apis.component.scss']
})
export class CancelRepeatedApisComponent {

  constructor(private apiService: ApiService) { }

  // Takes an number and calls a sample api using that
  async handleClick(num: number) {
    try {
      const res = await this.apiService.get(`https://dummyjson.com/products/${num}`)
      console.log("🔥 ~ handleClick ~ res:", res)
    }
    catch (err) {
      console.log("🔥 ~ handleClick ~ err:", err)
    }
  }
}



Enter fullscreen mode Exit fullscreen mode

In /src/app/services/api.service.ts



import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';

@Injectable({
  providedIn: 'root'
})

export class ApiService {
  constructor(private http: HttpClient) { }

  // get request using http , Make sure to import in modules
  public get(url: string) {
    return this.http.get(url).toPromise();
  }

}


Enter fullscreen mode Exit fullscreen mode

In /src/app/interceptors/cancel-same-apis.interceptor.ts



import { Injectable } from '@angular/core';
import { HttpRequest, HttpHandler, HttpEvent, HttpInterceptor, HttpResponse } from '@angular/common/http';
import { Observable, Subject } from 'rxjs';
import { tap, takeUntil } from 'rxjs/operators';

@Injectable()
export class CancelSameApisInterceptor implements HttpInterceptor {
  private cache = new Map<string, Subject<void>>();

  constructor() { }

  intercept(request: HttpRequest<unknown>, next: HttpHandler): Observable<HttpEvent<unknown>> {

    // Only cancel GET requests
    if (request.method !== 'GET') {
      return next.handle(request);
    }

    // if you want to check params as well then use request.urlWithParams.
    const url = request.url;

    // check if the request is already cached
    const cachedResponse = this.cache.get(url);

    // cancel any previous requests
    if (cachedResponse) {
      cachedResponse.next();
    }

    const cancelRequests$ = new Subject<void>();

    // cache the new request , so that we can cancel it if needed.
    this.cache.set(url, cancelRequests$);

    const newRequest = next.handle(request).pipe(

      // cancel the request if a same request comes in.
      takeUntil(cancelRequests$),

      // complete the subject when the request completes.
      tap((event) => {
        if (event instanceof HttpResponse) {
          this.cache.delete(url);
        }
      })
    );

    return newRequest;

  }
}


Enter fullscreen mode Exit fullscreen mode

Don't forget to add this interceptor in the provider in app.module.ts or your respective modules.



  providers: [{ provide: HTTP_INTERCEPTORS, useClass: CancelSameApisInterceptor, multi: true }]


Enter fullscreen mode Exit fullscreen mode

We have a map that holds url as key and cancelRequest$ as value.
We always check the cache before request. If cache exists we cancel previous requests using the value which is observable that is passed in the takeUntil.

takeUntil - takeUntilsubscribes and begins mirroring the source Observable. It also monitors a second Observable, notifier that you provide. If the notifier emits a value, the output Observable stops mirroring the source Observable and completes. If the notifier doesn't emit any value and completes then takeUntil will pass all values.

🕊 peace


If you are here it means you may have found this blog helpful. Just follow me @shrihari which will motivate to write more. You can make a Buttermilk 🥛. Small support comes a long way!

Subscribe If you want to receive these blogs in your mail from @Medium for free!

More things from me

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Terabox Video Player