DEV Community

Shrihari Mohan
Shrihari Mohan

Posted on • Updated on

Cancel repeated requests in Angular with interceptors

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

Top comments (1)

Collapse
 
raviagheda profile image
Ravi Agheda

Nice!