Easy caching with Rxjs
Tags: rxjs, angular, react, vue
Published: 2022-05-22 17:36:45

Caching is one of the most important things to learn in any Framework.

Today, I will explain the best and easiest way I have found to cache API response where ever you want (Local storage, memory, cookies, etc. ) using Rxjs.

**This way works with React, Vue, Angular, or any other Framework.**

## 1-Create API service 

API services is a great way to contain all of your API calls (if your are using Angular I think you already have one).

**In Angular you'll have**

```typescript
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';

@Injectable({
  providedIn: 'root',
})
export class TaskService {
  constructor(private http: HttpClient) {}

all_tasks():Observable<any>
  {
    return this.http.get<any>('example.com/api/tasks');
  }


}
```

**For Any other Framework**
 you need to install the following libraries if you don't have them already installed.

```
$ npm install axios rxjs axios-observable
```

and create your API service like this.

**Note:** If you don't understand, you can chech 
[my previous article ](https://dev.to/chrollo4ki/api-services-in-react-or-vue-using-rxjs-3lj7)

```typescript
import Axios, { AxiosObservable } from "axios-observable";

class TaskService {

    private static _instance: TaskService;

    public static get Instance() {
        return this._instance || (this._instance = new this());
    }

   all_tasks(): AxiosObservable<any> {
            return Axios.get<any>('example.com/api/tasks');
        }

}

export const _TaskService=TaskService.Instance;
```

## 2-Do the caching 
we have many options to choose for caching, At first I'll choose `Localstorage` then I'll show you how to store it in `memory`.

1-import the following operators `filter`, `startWith`, `tap`
 from `rxjs/operators`. 

2-add them to your API call using `pipe()` .
**For Angular**

```typescript
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { filter, startWith, tap } from "rxjs/operators";

@Injectable({
  providedIn: 'root',
})
export class TaskService {
  constructor(private http: HttpClient) {}

all_tasks():Observable<any>
  {
    return this.http.get<any>('example.com/api/tasks')
    .pipe(
                tap(res => localStorage['chash_key'] = JSON.stringify(res)),
                startWith(JSON.parse(localStorage['chash_key'] || '{}')),
                filter(res=>Object.keys(res).length !== 0), 
            );
  }


}
```

**For Any other Framework**

```typescript
import Axios, { AxiosObservable } from "axios-observable";
import { filter, startWith, tap } from "rxjs/operators";


class TaskService {

    private static _instance: TaskService;

    public static get Instance() {
        return this._instance || (this._instance = new this());
    }

   all_tasks(): AxiosObservable<any> {
            return Axios.get<any>('example.com/api/tasks')
 .pipe(
                tap(res => localStorage['chash_key'] = JSON.stringify(res)),
                startWith(JSON.parse(localStorage['chash_key'] || '{}')),
                filter(res=>Object.keys(res).length !== 0), 
            );

        }

}

export const _TaskService=TaskService.Instance;
```

congratulations. that's it 🥳🥳🥳...

## The explanation

We use here three operators..

1. **tap**
Used when you want to transparently perform actions or side-effects, such as logging.

2. **startWith**
Used when you want to emit value before any emissions from the source.

3. **filter**
Used when you want to filter emissions from the source.

## why do we use them?

we use `tap` to store the successful API response in `Localstorage` .

we use `startWith` to emit the cached value before the emissions arrive from the source, and we add `localStorage['chash_key'] || '{}'` to emit empty object in case the the cache store is empty.

we use `filter` to filter the final emissions, so in case the cache store is empty and the `startWith` operator returns empty object, the `filter` will block it.

If we don't add `filter` we might get bugs in the front end.

**Note:** by the way, if you are getting a raw array from the API like `[]`,  you can do                 `startWith(JSON.parse(localStorage['chash_key'] || '[]'))` and delete the `filter` operator.

**You can stop here if you want. I'll explain now how to cache in the memory:**

To cache in memory you have to do just few changes..

1-declare a private var type `any` in your class
2-store the API res in that var using `tap` operator.

your code will be like


For Angular 

```typescript
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { filter, startWith, tap } from "rxjs/operators";

@Injectable({
  providedIn: 'root',
})
export class TaskService {
  constructor(private http: HttpClient) {}

  private TasksCache: any;


all_tasks():Observable<any>
  {
    return this.http.get<any>('example.com/api/tasks')
    .pipe(
                tap(res => this.TasksCache = JSON.stringify(res)),
                startWith(JSON.parse(this.TasksCache || '{}')),
                filter(res=>Object.keys(res).length !== 0), 
            );
  }


}
```

**For Any other Framework**

```typescript
import Axios, { AxiosObservable } from "axios-observable";
import { filter, startWith, tap } from "rxjs/operators";


class TaskService {

    private static _instance: TaskService;
    private TasksCache: any;


    public static get Instance() {
        return this._instance || (this._instance = new this());
    }

   all_tasks(): AxiosObservable<any> {
            return Axios.get<any>('example.com/api/tasks')
 .pipe(
                tap(res => this.TasksCache = JSON.stringify(res)),
                startWith(JSON.parse(this.TasksCache || '{}')),
                filter(res=>Object.keys(res).length !== 0), 
            );

        }

}

export const _TaskService=TaskService.Instance;
```
The end...

Now, if you try to fetch data for the first time, your successful response will be cached and used for the next fetch you do.


## useful links 🔗 

## https://rxjs.dev/api/operators/tap

## https://rxjs.dev/api/operators/filter

## https://rxjs.dev/api/operators/startWith

{% embed https://github.com/zhaosiyang/axios-observable %}.