Caching HTTP Requests with Angular

Posted By : Deepak Rawat | 30-Nov-2018

When we are working with Angular application, we often make HTTP request to access the data from the API. But there are many cases when we make request to the same API in which case it is better to cache the response so that we can avoid the future request to the same API.

In this article we will see the strategy that can be used to cache the response from the APIs. Here we’ll be using angular HTTP interceptors, using the HTTP interceptors gives us a transparent way to handle caching.

Requirement:

  • Angular 5.1+

In an angular application the better way to achieve the caching is to use HTTP interceptors. As their name suggests it allows us to intercept HTTP messages, with the help of which we can change the HTTP requests or the HTTP responses. Hence it a better option for caching the HTTP requests. This method is better than the way, in which we use services for caching, this reduces the extra code in service and make it neat and clean.

After setting up the HTTP interceptors, all of our HTTP requests will go through this, and then we can do the caching. Let’s take a look  at how this would be achieved.

So for this we’ll create a separate file for interceptor and a service file for caching. Let see the interceptor file first.

 

import { Injectable } from '@angular/core';
import { HttpEvent, HttpRequest, HttpResponse, HttpInterceptor, HttpHandler } from '@angular/common/http';

import { Observable } from 'rxjs/Observable';
import { startWith, tap } from 'rxjs/operators';
import 'rxjs/add/observable/of';

import { RequestCache } from './request-cache.service';

@Injectable()
export class CachingInterceptor implements HttpInterceptor {
  constructor(private cache: RequestCache) {}

  intercept(req: HttpRequest<any>, next: HttpHandler) {
    const cachedResponse = this.cache.get(req);
    return cachedResponse ? Observable.of(cachedResponse) : this.sendRequest(req, next, this.cache);
  }

  sendRequest(
    req: HttpRequest<any>,
    next: HttpHandler,
    cache: RequestCache): Observable<HttpEvent<any>> {
    return next.handle(req).pipe(
      tap(event => {
        if (event instanceof HttpResponse) {
          cache.put(req, event);
        }
      })
    );
  }
}

To create an interceptor we have to implement the HttpInterceptor class, make sure that all the classes which implements the HttpInterceptor should have an intercept method. In this intercept method, first we try to fetch data from our cache. If we have a cached response data for that particular request then we’ll return a Observable with that cached data, if not then we send a request to the sever to fetch the data, in our case we are using sendRequest() method. This method also cache the request for further use.

Now let see the RequestCache service.

 

import { Injectable } from '@angular/core';
import { HttpRequest, HttpResponse } from '@angular/common/http';

const maxAge = 30000;
@Injectable()
export class RequestCache  {

  cache = new Map();

  get(req: HttpRequest<any>): HttpResponse<any> | undefined {
    const url = req.urlWithParams;
    const cached = this.cache.get(url);

    if (!cached) {
      return undefined;
    }

    const isExpired = cached.lastRead < (Date.now() - maxAge);
    const expired = isExpired ? 'expired ' : '';
    return cached.response;
  }

  put(req: HttpRequest<any>, response: HttpResponse<any>): void {
    const url = req.url;
    const cacheEntry = { url, response, lastRead: Date.now() };
    this.cache.set(url, cacheEntry);

    const expired = Date.now() - maxAge;
    this.cache.forEach(expiredEntry => {
      if (expiredEntry.lastRead < expired) {
        this.cache.delete(expiredEntry.url);
      }
    });
  }
}
 
The RequestCache service implements a get and put method, and it works with a map. Note here we are also removing the old cache map, so that stale data should not be there in the cache.

Now our next step is to enable the HTTP interceptor, for this let’s look at the app.module.ts file.

 

import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http';
import { RequestCache } from './request-cache.service';
import { CachingInterceptor } from './caching-interceptor.service';

// ...

providers: [
    RequestCache,
    { provide: HTTP_INTERCEPTORS, useClass: CachingInterceptor, multi: true }
  ],

// ...
 

Now you can see the ApiService is only responsible for the request and not for caching the response. For caching we are using the separate service file, and with the help of HTTP interceptors every HTTP request will be cached.

 

 

About Author

Author Image
Deepak Rawat

Deepak is a Web and Mobile application Sr. Lead Frontend developer and good working experience with JQuery , AngularJS , Javascript and PhoneGap. His hobbies are listening to music and photography.

Request for Proposal

Name is required

Comment is required

Sending message..