A Better Way to Optimize Data Fetching

cached-fetch optimizes your app’s data fetching, whatever your app platform is

Woohyun Jang
3 min readJul 10, 2021

If you have developed React app, you probably heard of ‘SWR’. SWR caches fetched data. So the app calls the same fetch, cached data is returned without network communication.

But it only works React hooks. If you want to use SWR, you must call the ‘useSWR’ API. And also you can customize only the ‘fetch’ part, like that.

const fetcher = (...args) => fetch(...args).then(res => res.json());
function App() {
const { data, error } = useSWR('/api/user/123', fetcher); ...

You just can input two parameters path and fetcher. You can’t understand how caching works.

cached-fetch can works even the development environment is vanilla JavaScript. And you can select the storage what the data is cached. Of course, default storage is provided. it is a SessionStorage that is a window API.

  1. CachedFetcher Component gets Storage and Fetcher(line 22).
  2. When the fetch method is called, the cachedFetcher instance finds the cached data from storage.
  3. If the cached data do not exist or expired, It fetches to the server. Server response data is cached and sent to the client.
  4. If the cached data is valid, cached data will be returned without network communication.

Sometimes you want to request to the server for real-time data. So you can set the expired time to data and discard cached data.

To use this library, you have to input Storage and Fetcher. These are implemented considering DI(Dependency Injection).

At first, let see the Storage.ts file:

You just extend the Storage interface and follow its details:

  1. core: Storage core engine
  2. getItem: Get data from storage. The identifier is key.
  3. setItem: Set data from storage. The identifier is key.
  4. removeItem: Remove data from storage. The identifier is key.

Let’s see the SessionStorage. It is a default Storage implementation:

class SessionStorage extends Storage {  core: globalThis.Storage;  constructor() {
if (!sessionStorage) { throw new Error('sessionStorage not exists.'); } this.core = sessionStorage; } getItem(key: string): CachedItem { const cached = this.core.getItem(key); if (typeof cached !== 'string') { return null; } const cachedItem = JSON.parse(cached); if (!cachedItem.data || !cachedItem.expiredAt) { return null; } return cachedItem; } setItem(key: string, item: CachedItem) { this.core.setItem(key, JSON.stringify(item)); } removeItem(key: string) { this.core.removeItem(key); }}

sessionStorage of window stores data by key. The key and value are only string type. So setItem stores the data after stringify, and getItem returns the data after parse.

Fetcher is here:

It has one method, fetch. The fetch method gets URI, method, header. Not difficult.

In the HTMLFetcher(A implementation of Fetcher) case, It just calls the HTML fetch method.

// HTMLFetcher.tsclass HTMLFetcher extends Fetcher {  core: Window = window;  fetch(fetchParam: FetchParam): Promise<unknown> {    return this.core.fetch(fetchParam.uri, { headers: fetchParam.headers });  }}

The CachedFetcher runs on only GET api call. And It’s lacking point that judgment of the data’s latest depends on expiredAt . Any feedback would be welcome:)

More content at plainenglish.io