How to implement Caching by HttpInterceptor in Angular
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 :)