Rxjs data cache

A rxjs update to simple in-memory cache for Angular data-providers

Andy Dłubak
Aug 31, 2018 · 5 min read

Background

In my recent article, I have described a convenient way of caching repeated data calls and its payload in a simple in-memory data cache. If you haven’t seen it and want to get the most out of this article, I suggest you give a read (the concept is pretty simple, so it shall be quick). The described concept is based on Angular 2+, but it can be easily adapted to any framework.

As a followup to the original article, in this one I’ll describe how to avoid keeping two types of caches: one for data and one for calls, by using rxjs in a slightly more advanced way.

The concept was initially created by my friend Luke Czernal, based on the data-provider caching from the original article, so before we kick off, just a big thanks to Luke for this interesting idea.


Use Case

To fully understand the use case of the described solution, please refer to the detail description placed in previous article.

A short description can also be found here:

Let’s try to imagine an application, that will need to pull data at the same time from the same endpoint. Such situation can happen, for example, when we have a list of some objects received from an API with one data-call, and each of those objects contains reference to other API endpoints, for some details data or related object.
Data flow would look like that:

Request data from first-endpoint: list of objects-A is received

Each of received objects-A contains a reference to object-B from second-endpoint

For each of received objects-A, request detail data by making a call to objects-B second-endpoint

We can now see, that in case when more than one of objects-A contains the same reference to object-B, the same data call will be made. And this is a model example where services in-memory cache is handy.


Data structures and setup

In the original article, we have created 2 types of caches in our data-provider: observable and data cache, just like that:

Both of those are maps data structures, where club.id is a key in the map, and the corresponding value is a Club class instance (an object with data) for data cache or an Observable of a Club class instance for observable cache.

Therefore, a simplification to this code seems obvious. The observable cache is the only cache that we need, as we can always get the data out of an observable.

To illustrate this simplification, we will create a simple typescript & rxjs code, that can be later adapted to our Angular data-provider form previous article.

Let’s kick off with the basic data type that our application will use, by creating a Club model:

The Club class consists of some simple properties for the class, like id, name and town. Worth mentioning is the way that the instance is created, by using Object.assign ES6 method. More reference in the official documentation.

Rxjs data cache

Once we have the model, we can start building our cache. We will need to import rxjs elements as well as Club model. Let’s start with such imports.

So we imported Observable class and some methods for it, like publish and of. Also, an import of Club class was added.

With all that we need being imported, we can create a cache. It will initially be an empty object. As mentioned earlier, it shall be a map data structure, with values as Observable of a Club class instance.

In the original data-provider, we had one public method that allowed to pull for club data: getClub(id: number). This example shall simulate that also, so let’s create such function, however, this time this function will always return an element of the cache map:

What the getClub() does is actually very simple. On line 2 it sets a new map key-value pair in the clubCache in two possible ways:
- in a case when such key is already present in the cache, it is set to the existing one
- in a case when such key is not present in the cache, it sets whatever is returned by the fetchClub(id) function

At the end it simply returns the cached value.

So, the fetchClub(id) must also return an Observable of Club class. Let’s take a look what and how this function does:

The fetchClub() returns whatever http.get method returns. In Angular it returns an Observableof given kind, however, we don’t assume to have Angular in this example. So let’s create a mock version of http just to be used in this example.

So we have a mock http object, that has get method, that will return Observable of raw-data, that looks just like a Club class. Sweet!

But let’s get back to fetchClub(id). It returns an Observable of raw-data, so we need to map this raw data into our Club class with the mapClub() function.

.map(rawData => mapClub(rawData)

It is a very simple method, that just wraps whatever data it receives with a Club class. Simple.

After mapping raw-data with Club class, we know that the returned observable is a Club class instance.
In the original article, we used share() method of Observable to make sure all concurrent subscriptions are using the same observable. However this time, we are not following that path, in favor to use publish() and refCount() methods.

  • publish() — turns our regular Observable into connectable observable. Connectable observables is similar to regular, but it allows to emit values at any chosen time. That way (in our use case) new subscriptions (created by new Angular components) with fall into the same observable (docs).
  • refCount() — turns connectable observable to behave like a regular Observable. It can be applied only on connectable observable, and due to the usage of this method, all the subscriptions we have will connect to the same observable (docs).

With such setup, we are able to test our solution. First lets just create a small helper function that will print received club id when data-call is finished:

Simulation of usage

At the end of our code, we can simulate the creation of Angular components. As each of those components requires to pull club data — simply calling getClub() function with given club-id is enough.

This shall result with a console output, with just 3 notify() , and display of full cache just like here:

If you want to play around with this, here is a working example:

PS.

I hope you enjoyed reading this article, and also hope it will help you level up your skills. If you want to see a follow up on this topic, (as well as other front-end stuff) don’t forget to follow me on Medium. In next article, I will show you how to incorporate this solution into Angular application, and make it more reusable in multiple data-providers.

Andy Dłubak

Written by

I design, develop, and test web & mobile apps. Currently, I’m a CTO at Cleria A.S. working with Angular, Django, and most of all people.

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade