I am looking to implement caching for HTTP parallel requests by sharing the observable and also storing the response in a Map object.
caching-interceptor.service.ts
import { HttpEvent, HttpHandler, HttpInterceptor, HttpRequest, HttpResponse } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Observable, of } from 'rxjs';
import { tap, finalize, share } from 'rxjs/operators';
@Injectable()
export class CachingInterceptorService implements HttpInterceptor {
public readonly store = new Map<string, HttpResponse<any>>();
public readonly queue = new Map<string, Observable<HttpEvent<any>>>();
constructor() {}
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
// Don't cache if it's not cacheable
if (req.method !== 'GET') {
return next.handle(req);
}
// Check if there is a pending response for this request
const cachedObservable: Observable<HttpEvent<any>> = this.queue.get(req.urlWithParams);
if (cachedObservable) {
console.info('Observable cached');
return cachedObservable;
}
// Check if there is a cached response for this request
const cachedResponse: HttpResponse<any> = this.store.get(req.urlWithParams);
if (cachedResponse) {
console.info('Response cached');
return of(cachedResponse.clone());
}
// If the request is going through for the first time,
// let the request proceed and cache the response
console.info('Request execute');
const shared = next.handle(req).pipe(
tap(event => {
if (event instanceof HttpResponse) {
console.info('Response reached');
this.store.set(req.urlWithParams, event.clone());
}
}),
finalize(() => {
// Delete pending request
this.queue.delete(req.urlWithParams);
}),
share()
);
// Add pending request to queue for caching parallel requests
this.queue.set(req.urlWithParams, shared);
return shared;
}
}
Is this implementation of observable caching correct?
I have some doubts about what happens if the observable is deleted during the finalization of the request and someone has subscribed to it.
Side Note: This is just an example and does not include cache expiring/invalidation logic.