How to implement Caching by HttpInterceptor in Angular

fr4nk.xyz
myorder
Published in
4 min readFeb 19, 2023

Caching is an important technique that can improve the performance of web applications by reducing the number of requests made to the server. In this blog post, we will explore how caching can be implemented in Angular using interceptors.

Sequence diagram

+---------------+     +-------------------+     +---------------+
| HTTP Request | | CachingInterceptor| | HTTP Response |
+---------------+ +-------------------+ +---------------+
| | |
| canCache() == true | |
| cache hit | |
| <------------------ | |
| | |
| canCache() == true | |
| cache miss | make HTTP request |
| -------------------> | and cache response |
| | ------------------> |
| | |
| | |
v v v
+---------------+ +-------------------+ +---------------+
| HTTP Request | | CachingInterceptor| | HTTP Response |
| | | (cache = new Map)| | |
+---------------+ +-------------------+ +---------------+

Angular CLI for Generate file and implement class intercept

ng generate interceptor <name>
  • This case example using name file that caching
ng generate interceptor caching

1. We import the necessary dependencies from Angular and RxJS.

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

2. We create an @Injectable() class called CachingInterceptor that implements the HttpInterceptor interface.

@Injectable()
export class CachingInterceptor implements HttpInterceptor {

3. We declare a private variable called cache that is initialized as an empty Map.

private cache = new Map<string, any>();

4. We define the intercept() method, which takes in the HttpRequest and HttpHandler objects and returns an observable of type Observable<HttpEvent<any>>.

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

5. We check whether the request can be cached by calling the canCache() method, which returns a boolean value. If the method returns false, we simply return the request using next.handle(request).

if (!this.canCache(request)) {
return next.handle(request);
}

6. If the request method is not GET, we return the request without caching it.

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

7. We check whether the response for the request is already cached in the Map. If it is, we return the cached response as an observable of type of().

const cachedResponse = this.cache.get(request.url);
if (cachedResponse) {
return of(cachedResponse);
}

8. If the response is not cached, we forward the request to the next handler using next.handle(request). When the response is received, we save it in the Map cache and return the response as an observable.

return next.handle(request).pipe(
tap(response => {
this.cache.set(request.url, response);
})
);

9. We define a private method called canCache(), which takes in a HttpRequest object and returns a boolean value indicating whether the request should be cached. In this example, the method checks whether the request URL contains the string 'api/product'.

private canCache(request: HttpRequest<any>): boolean {
return request.url.includes('api/product')
}

Overall, this code intercepts HTTP requests and caches their responses if they meet the criteria defined in the canCache() method. This can help improve the performance of the application by reducing the number of requests sent to the server.

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

@Injectable()
export class CachingInterceptor implements HttpInterceptor {

private cache = new Map<string, any>();

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

// handle by path url
if (!this.canCache(request)) {
return next.handle(request);
}

// handle by HTTP Method
if (request.method !== 'GET') {
return next.handle(request);
}

// cached hit
const cachedResponse = this.cache.get(request.url);
if (cachedResponse) {
return of(cachedResponse);
}

// cached miss
return next.handle(request).pipe(
tap(response => {
this.cache.set(request.url, response);
})
);

}

private canCache(request: HttpRequest<any>): boolean {
return request.url.includes('api/product')
}
}

next step, We then define a module called MyModule and import the HttpClientModule. We also define a provider for the HTTP_INTERCEPTORS token, which is an array of objects that map to each interceptor. In this case, we define an object for the CachingInterceptor that specifies its class and that it should be used as a multi-provider (meaning it can be used alongside other interceptors).

import { NgModule } from '@angular/core';
import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http';
import { CachingInterceptor } from './caching.interceptor';

@NgModule({
imports: [HttpClientModule],
providers: [
{
provide: HTTP_INTERCEPTORS,
useClass: CachingInterceptor,
multi: true
}
]
})
export class MyModule {}

Bonus

In this updated version, we added a clearCache() method to the CachingInterceptor. This method simply calls the clear() method on the Map object to clear all the cached responses.

public clearCache(): void {
this.cache.clear();
}

You can then use this method to clear the cache from your Angular component or service:

import { Component } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { CachingInterceptor } from './caching.interceptor';

@Component({
selector: 'app-my-component',
templateUrl: './my-component.component.html',
styleUrls: ['./my-component.component.css']
})
export class MyComponent {

constructor(private http: HttpClient, private cachingInterceptor: CachingInterceptor) {}

public clearCache(): void {
this.cachingInterceptor.clearCache();
}

public getProduct(): void {
this.http.get('api/product').subscribe(response => {
console.log(response);
});
}
}

We added a clearCache() method to the MyComponent class that calls the clearCache() method on the CachingInterceptor. You can then call this method from your component or service to clear the cache as needed.

Good Luck :)

--

--

myorder
myorder

Published in myorder

How development team at My Order doing in-work and after-work

fr4nk.xyz
fr4nk.xyz

Written by fr4nk.xyz

I'm passionate in Information Processor and interested Computational linguistics & Embedded System