How to cache the result of an HTTP request with Angular?

Alain Chautard
Angular Training
Published in
3 min readJul 24, 2020

--

Single-page web applications rely on AJAX to make HTTP requests to backend servers to get their data.

Most of the time, we want to make sure that the data we are getting from the server is fully up-to-date, so we don’t cache any of it and just request the data as often as we need it.

A typical example looks like this:

getData(): Observable<MyData> {
return this.http.get<MyData>('https://mydata.com');
}

That said, there is data that is unlikely to change while the user is interacting with your application. For instance, you could have a list of countries or states in a dropdown. That data can be fetched just once, and subsequent use cases of said dropdown would access the cached data to retrieve it.

Pass your Angular certification exam today

In our previous code example, every time we call getData(), we’re getting a new observable, and subscribing to that observable will trigger the HTTP request again.

Instead, let’s create our Observable just once and subscribe to it whenever we want that data:

data$: Observable<MyData>;
myData: MyData;

constructor(http: HttpClient) {
this.data$ = http.get('https://mydata.com');
}

getData() {
this.data$.subscribe(data => this.myData = data);
}

Does the above code improve anything? It actually doesn’t. This is counter-intuitive, but every call to .subscribe() is going to fire that HTTP request. With RxJs, every single cold observable behaves that way: Any new subscriber starts a new “stream” and as a result, in our case, a new HTTP request.

One option: Creating our own Subject

We could use create a Subject, more specifically a ReplaySubject (more info about RxJs subjects in this tutorial), to emit and cache that data.

Something along those lines:

data$: Observable<MyData>;
mySubject = new ReplaySubject(1);

constructor(http: HttpClient) {
http.get('https://jsonplaceholder.typicode.com/users')
.subscribe(data => this.mySubject.next(data));
}

getData(): Observable<MyData> {
return this.mySubject.asObservable();
}

While the above code will make that HTTP request only once, and subsequent calls to getData() will get cached data, this syntax is complex and not what I would recommend.

A better option: Using shareReplay

RxJs has an operator that does exactly what we’ve done above, only in a much simpler way. That operator is shareReplay.

shareReplay will save the latest value emitted (here a value of 1 indicates that only that last value emitted is saved) and replay that value to any new subscriber

Here is how we can turn an Observable into one that automatically caches the latest data received and replays it to any new subscriber:

this.data$ = http.get('https://mydata.com').pipe(
shareReplay(1)
);

That single operator just turned our Observable into a ReplaySubject that behaves exactly like what we’ve seen in our previous example. How neat is that?

The full code for my example now becomes:

data$: Observable<MyData>;
myData: MyData;

constructor(http: HttpClient) {
this.data$ = http.get('https://mydata.com').pipe(
shareReplay(1)
);
}

getData() {
this.data$.subscribe(data => this.myData = data);
}

On a side note, I do not recommend subscribing to observables in your component the way I did in this post. Instead, you should be using the async pipe, as explained here. The above code is for demo purposes only.

Here is the full code for my solution on Stackblitz (https://stackblitz.com/edit/ng-http-caching?file=src%2Fapp%2Fapp.component.ts):

My name is Alain Chautard. I am a Google Developer Expert in Angular, as well as founding consultant and trainer at Angular Training where I help development teams learn and become proficient with Angular.

Need help with Angular? Let’s schedule some time to talk.

If you enjoyed this post, please clap for it or share it. Your help is always appreciated.

--

--