Cancel repeated requests in Angular with interceptors
Tags: webdev, javascript, angular, tutorial
Published: 2023-04-16 17:33:04

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

{% youtube https://youtu.be/_j19lZVwjGM %}

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

---

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

```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>
```
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`
```typescript
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)
    }
  }
}

```

In `/src/app/services/api.service.ts`
```typescript
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();
  }

}
```

In `/src/app/interceptors/cancel-same-apis.interceptor.ts`
```typescript
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;

  }
}
```

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

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

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` - `takeUntil `subscribes 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 🥛](https://www.buymeacoffee.com/shriharimohan). Small support comes a long way!

Subscribe If you want to receive these blogs in your mail from @[Medium](https://medium.com/@shriharimohan) for free!

## More things from me
{% embed https://dev.to/shrihari/redis-bull-easy-background-jobs-in-nodejs-50le %}

{% embed https://dev.to/shrihari/how-i-built-a-chrome-extension-that-finds-ghost-css-ghost-css-detector-4aj0 %}

{% embed https://dev.to/shrihari/nodejs-cron-handling-overlapping-tasks-like-a-noob-c32 %}