December 28, 2018

The fetch() is becoming de-facto standard from performing HTTP requests in browsers, being very widely used in place of the old XHR objects.

Sometimes though, when you’re fetch() ing data, you don’t want to actually hit the network each time this happens. It begs for a simple cache of results.

Since its API is very simple, we can easily wrap it in a bit of custom logic and conditionally fire off the actual request, depending of the cache state. In this particular case we’ll treat the URL of the request as the cache key. In a more complex scenario, we need to hash out the URL, headers and body of the request into a distinct and predictable key that can be used for caching the response.

Here’s the basic code that does it:

const store = new Map ( ) const cachedFetch = url => store . get ( url ) ? new Promise ( resolve => { resolve ( store . get ( url ) ) store . set ( url , store . get ( url ) . clone ( ) ) } ) : fetch ( url ) . then ( response => store . set ( url , response . clone ( ) ) )

The snippet is very simplistic but illustrates the basic mechanism of the caching.

A couple of notes:

For storage here we use a Map but it could easily be replaced with an empty object or any custom syncronous store that supports get()/set() interface.

but it could easily be replaced with an empty object or any custom syncronous store that supports interface. We store a clone of the original response returned by fetch() , otherwise the body cannot be reused.

, otherwise the body cannot be reused. We don’t cover here network failures and we don’t invalidate cache in case they happen. This is crucial in production apps.

const fetchData = async ( ) => { const response = await cachedFetch ( 'https://baconipsum.com/api/?type=meat-and-filler' ) return ( await response . json ( ) ) [ 0 ] } fetchData ( ) fetchData ( )

Further improvements:

Use something more involved than URL in the cache key (Headers, HTTP Method, Request Body)

Perform proper network error checking and non-200 responses

API for forcing the network touch — useful when you need to refetch the response from scratch

Now we support only default options for cachedFetch() — start supporting the whole Fetch API.