NgRx effect use to cache Get request

Thomas Laforge
5 min readNov 22, 2021

--

I love NgRx and everything around NgRx, that’s why I decided to write a series of articles about real life issues resolved with the help of NgRx. This article will be the first one of the series.

What is NgRx ?

NgRx is a reactive state management library for Angular applications. For those who have never heard of it, I invite you to read the official site here and come back to this article afterwards. This series is meant for developers with a minimal understanding of NgRx and Angular.

What is NgRx Effect ?

NgRx effect is a feature of NgRx which lets you run any application side effect in isolation (like http calls, …) This means, it won’t block your UI while waiting for your backend or any other async operations to respond. When the response arrives, a NgRx action will be thrown and your store and UI will be updated and refreshed.

What is Caching ?

Caching is the term used when you save your data on your frontend application to avoid unnecessary backend http calls. If your data doesn’t change often, you don’t need to fetch your backend at each request. And you will get a better user experience.

Let’s get started

Let’s create a simple application where our user can click on a button to ask for our product catalog. This application will of course use NgRx as a state management library.

Set up

To start, we need a state with a list of products. We will add a loading indicator and error message, as well as a log list to see if the data is fetched from the backend or from the store (Will be useful for the demo!).

We also need a simple list of actions and a reducer to update our store:

We will dispatch loadProduct action from our component to trigger our effect. At the same time, it will update the loading indicator and log array in the store.

We also need a success and failure action to handle both states of our http responses. loadProductSuccess will update our product list and set the loading indicator to false. loadProductFailure will set the loading indicator to false as well and store the error.

Now that the store is set up, let’s create our effect to handle our http calls.

Solution 1

Let’s start by creating a very simple effect which calls our backend every time the user asks for our list of products.

This effect triggers the http call behind the getProducts() function every time the loadProducts action is dispatched. And as described above, you can see that we return the loadProductsSuccess on http call success and the loadProductsFailure in the catchError of our http call.

result of no caching
multiple calls without caching

This is working fine but the product list is fetched at every single click on the load button. This is not optimal. Thus it will be better for the user to cache it and get it from the store if the list is already loaded. Let’s do better!

Solution 2

Our second solution will check if the products are already in the store. If so, we won’t do anything (because the products are already loaded in the store), and if not, we are going to fetch our backend.

When loadProduct$ is triggered, we get our products list from the store. If the list is empty, we fetch our backend, otherwise we do nothing.

The concatLatestFrom operator is used to get our products list from the store. (Same operator as withLatestFrom from rxjs, but with concatLatestFrom the selector is only trigger when asked, which can have a big impact on performance if your selector is doing some heavy computation)

We added one new action: NO_OP_CACHE (see implementation below). This is because we must return an action from our effect. This action is not doing anything. But in our case, I’m logging it to the state to see that we are not fetching the back. (I’m logging all actions for the sake of this article, but in real life, you should use the redux devtool to debug your action flow and not log all actions inside the store).

multiple calls with caching

You can see, after our first click, the product list is coming from the store and no http calls are fired anymore.

This is getting better, but we can improve it one step further. We can add an expiration cache time. This will refresh our list every X minutes (or hours depending on your use case).

Solution 3

Our third and last solution will add an expiration time to our effect. We are not going to fetch the backend for 1 minute and after that time, we will refresh our list to check if new products have been added or updated.

This effect is a bit more complicated. We check if cacheExpirationTime is null or has expired, and if so we fetch our data, and we reset our cacheExpirationTime with a new value. If expiration time is not expired, we trigger our NO_OP_CACHE action(like the previous case) and do nothing.

In the effect, two utility functions are needed: isPast and newDateInXMinutes.

As you may have noticed, we are using an InjectionToken to inject our cache expiration delay. For this simple example, this seems over engineered, but if your application is fetching multiple data in multiple different slices of state, we can set the expiration time in only one place to stay DRY. In our case, the expiration time is hard set on our app.module. But you can set it in an environment file and have different expiration time between environments.

multiple calls with expiration cache time

As you can see, cache has expired after a certain delay and the backend has been fetched again.

We can still improve our solution with push notification to update in real time our cache datas. This is a topic for a following article where I will talk about using the design of NgRx effect, and Firebase messaging to create a nice decouple push messaging solution. Stay tuned!!

You can play with it on Stackblitz here.

You can reach me on my twitter @laforge_toma or leave me a comment.

I hope you liked my first article.

--

--

Thomas Laforge

Software Engineer | GoogleDevExpert in Angular 🅰️ | #AngularChallenges creator