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
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
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
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
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..
tap
Used when you want to transparently perform actions or side-effects, such as logging.startWith
Used when you want to emit value before any emissions from the source.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
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
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
zhaosiyang / axios-observable
Use axios in a rxjs way. use Observable instead of Promise
axios-observable
Observable (as opposed to Promise) based HTTP client for the browser and node.js
Want to use axios in a rxjs (observable) way? There we go!
This API of axios-observable is almost same as API of axios, giving you smooth transition. So the documentation mirrors the one of axios (A few exceptions will be cleared pointed out).
Features
- Make XMLHttpRequests from the browser
- Make http requests from node.js
- Supports the Observable API
- Intercept request and response
- Transform request and response data
- (NEW in v1.1.0) Cancel requests through unsubscribe
- Automatic transforms for JSON data
- Client side support for protecting against XSRF
Installing
Using npm:
note: axios
and rxjs
are peer dependencies.
$ npm install axios rxjs axios-observable
Example
Performing a GET
request
import Axios from 'axios-observable';
// or const Axios = require('axios-observable').Axios;
// Make a request for a user with a given ID
Axios.get('/user?ID=12345')
.
….
Top comments (0)