Angular API Caching
Update (Date Here): change the implementation to use localForage library.
This article will take you through the way to efficiently handle the HTTP client request and cache them inside different browser storage.
For clarity about what Iโm going to talk about, the full project is available to browse through Github.
What Is Caching
Caching is a way to store data that is often in use (HTTP response in our case) in storage to quickly access it later on. So instead of asking for the same data twice, you store a copy of it, and later on, you got that copy instead of the original.
How It Works
- A request made to server X to get data Y.
- The request goes through the cache to check if there's a copy from point 4.
- No copy found so the request will be delegated to the actual server. otherwise, jump to point 5.
- The server returned data Y and pass a copy through the cache.
- The request initiator gets the data to deal with it.
- Data will remain in cache till it expires or the cache is cleared.
That is the basic flow of how cache works.
There're many advantages of caching in general so I'll just point out some of what the Front End interested in
When/Why To Use Cache
- Reduce the number of requests.
- Store response that doesn't frequently change.
- Request that the application addressing frequently.
- Improve responsiveness by retrieving the response immediately.
- Show some content while you requesting the fresh content.
- Show some data while there's no internet connection to provide an offline experience
And of course, there might be other use cases depends on your custom usage.
Where To Store The Cache Data
- LocalStorage
- SessionStorage
- IndexedDB
- WebSql
- Cache API
- Cookie
- And InMemory ๐
We will use localForage library that acts as one interface for most of the mentioned storages, of course, you can use whatever you see suitable.
you can install from here npm install localforage
Example Usage
import { Injectable } from '@angular/core';
import { HttpClient, HttpResponse } from '@angular/common/http';
@Injectable()
export class ExampleService {
constructor(
private httpClient: HttpClient,
) { }
getData() {
const url = '/endpoint-url';
return this.httpClient.get(url);
}
}
Sample showing an example service that gets data from a server
import * as localforage from 'localforage';
const store = localforage.createInstance({
driver: localforage.INDEXEDDB, /** Here where you can change the storage */
version: 1.0,
storeName: 'cache',
});
Configure a store, I'm using IndexedDB but feel free to use anything else.
Going back to the start.
- Initiate a request to server X to get data Y. (the request initiator is
getData
method )
- The request goes through the cache to check if there's a copy from point 4.
- No copy found so the request will be delegated to the actual server. otherwise, jump to point 5.
- The server returned data Y and pass a copy through the cache.
- The request initiator gets the data to deal with it.
- Data will remain in cache till it expires or the cache is cleared.
getData() {
const url = 'https://jsonplaceholder.typicode.com/todos/1';
return defer(() => store.getItem(url) /** step 2 */)
.pipe(switchMap((cachedData) => {
if (cachedData === null || cachedData === undefined) { /** step 3 */
return this.httpClient.get(url) /** step 1 */
.pipe(switchMap((newData) => store.setItem(url, newData)));
}
return of(cachedData); /** step 5 */
}))
}
defer
used to create an Observable from Promise.
More about caching.
Caching Strategy
You might not want always to return the data from the cache perhaps you want only to hit the cache if the server call failed in this case you have something to show to the user or you want to hit the cache and the server so you have something to present quickly from the cache in the same time you're getting the fresh data from the server.
-
Cache First (Cache Falling Back to Network)
This implies that the cache has a higher priority to fetch data from. what we already did above.
-
Network First (Network Falling Back to Cache)
As opposite, fetch data from the network and if an error occurred or no internet connection use cache as a fallback
-
Stale-While-Revalidate
Return data from the cache while fetching the new data from the server.
Read more on web.dev
Resources
https://web.dev/service-worker-caching-and-http-caching/