Cache me if you can !!

Ritik Rishu
Beginner's Guide to Mobile Web Development
6 min readMar 11, 2018

With the fast and vast ongoing popularity and adoption of PWAs, soon the norm is going to be Web Apps and PWAs mostly around with a lesser number of traditional website modal web portals.

I see people often misinterpret PWA is all about Service Workers, well it is but Service Workers are more like the backbone of it’s capabilities while PWA itself is made using a bunch of different Web APIs together to extract some sensible feature out of it. One of these APIs is the CacheStorage API which is also our point of discussion. This article assumes you’ve basic familiarity with Service Workers, it’s lifecycles and offline web apps, if not, I would suggest you to take a look at this cool thing the web has for quite some time now!
When we think of a PWA, the impression that comes first to our mind is offline. The idea behind this is that once the Service Worker gets installed and activates, it can intercept network requests made from our origin or “the service worker scope” and serve contents for our app to the browser from some local cache. But before all that, some secure mechanism is required to manage storing, retrieving and updating app related content. Here comes the requirement and role of CacheStorage API. Caching and serving assets from the local system not only provides better UX when a device is offline or in slow network area but also helps reducing data usage and bandwidth on client and server respectively.

CacheStorage enables the application to store a request-response list under category keys. This means you can define categories of data you want to store for your app, and then under those categories or keys, store a list of requests mapped to their corresponding responses for future use. The asynchronous, Promise based nature of CacheStorage and design & behaviour of Service Worker events and lifecycle along with fetch which is also Promise base API makes them such good fit to use together that it has lead to a series of patterns to store and serve contents depending on their role and requirement to the app. But even without Service Workers, CacheStorageis still a good fit for storage purposes, mainly because of it’s nonblocking async nature.

Although CacheStorageis defined under the Service Worker spec, it falls underWindowOrWorkerGlobalScope, which means it can be accessed from the window or any type of worker scope (e.g. Service Worker, Web Worker, Shared Worker).

Now that we have ground understanding of what CacheStorage API is, let’s look into how to use it using the functions it exposes to the user and then some patterns around it.
Before we start, I want us to be clear about few terminologies-

  • CacheStorage — the API itself. accessible with variable caches on window as well as worker scope.
  • Cache — an object representing one single category or request-response list stored under CacheStorage.

Generally while caching resources you’d want to categorize them according to type or usage such as static assets, image resources, dynamic API responses.

Creating/Opening cache in store

Your domain/origin can have multiple named cache objects in the store. The caches object that implements CacheStorage has a method, open. open takes in CacheName as a parameter and returns a Promise which resolves with a cache object. If a cache of the same name already exists, open returns reference to the cache, otherwise, it creates a new cache and returns it’s reference.

Retrieving all cache keys from store

caches.keys() resolves with a Promise containing all Cache names or category names in a sequence. This method is handy to retrieve all caches when Service Worker is updated and delete all non required data from the previous version, helps in keeping origin cache clean.

Checking if a named cache exists in store

caches.has is a function exposed by CacheStorage that takes in cacheName as a parameter and returns a Promise that resolves in to true or false depending on whether the CacheStorage contains any cache with the supplied name.

Retrieving items from store

CacheStorage has a match method, accessed through caches.match. match takes a required parameter RequestInfo and an optional CacheQueryOptions parameter. RequestInfo is the request for which the response is stored. For example, an image resource can be stored against request with URL user/abc/profile with Content-Type image/jpeg .
CacheQueryOptions are set of configurations to alter the filtering w.r.t providedRequestInfo . It has four values-

  • ignoreSearch- defaults to false. If set to true, indicates that we wish to exclude the query params from RequestInfo URL.
  • ignoreMethod- defaults to false. If set to true, indicates that we wish to prevent the matching operation from validating the Request method.
    Note: only Get and Head are stored in the cache.
  • ignoreVary- defaults to false. If set true, indicates that we wish to include responses with VARY headers as well.
  • cacheName- name of the specific Cache, we want to look into instead of looking through all cache in storage.

Deleting from store

caches.delete takes a string i.e. cacheName and returns a Promise that resolves to true or false depending on whether the operation was successful or not. You can use this when your site’s Service Worker gets updated to clear up the whole old version assets cache that is no longer required.

If you’ve used databases before, then you can relate with caches object or CacheStorage is the container or the database itself and the object that caches.open resolves into i.e.cache represents one table in a database.

Cache interface is very much similar to that of CacheStorage, let’s have a look at cache as well.

Add items to cache

  • cache.add method accepts a request. From the passed request, response is fetched over the network and then stored as a value against request as the key.
  • Another similar method is available, cache.addAll which takes an array of Request that is fetched over the network and respective response is stored against their Requests in store. These are helpful in prefetching static resources of our site. Often used with Service Worker update event to update new app resources.
  • putcache.put allows to pass both request and response objects that are stored as key and value respectively. Often you’d use cache.put in Service Worker’s fetch event handler to store responses in cache for serving in future.

Retrieve items from cache

  • match — match takes a RequestInfo and an optional CacheQueryOptions as parameters and returns the first matching object which can be of any type if found or undefined if no match is found, wrapped in Promise. This is same as CacheStorage.match the only difference is that this is called on cache object and hence cacheName option in CacheQueryOptions is ignored in this call.
  • matchAll — matchAll takes a RequestInfo and an optional CacheQueryOptions as parameters and returns a Promise that resolves into FrozenArray of all responses from the cache that matches this request. This is same as CacheStorage.match the only difference is that this is called on cache object and hence cacheName option in CacheQueryOptions is ignored in this call.

match and matchAll here are called on cache object so the search operation is executed only in the specific cache, not the whole CacheStorage

Retrieving all keys from cache

cache.keys() resolves in a Promise containing Frozen array of Request. These are all the keys from the cache can be used to iterate and filter objects in store or clean up some items.

Deleting from cache

cache.delete takes a Request object and an optional CacheQueryOptions to identify and delete some request-response pair from the cache. Since CacheStorage performs all operations manually, i.e. only when called by the user, it is very critical to wisely use delete method and clear up any data that is no longer relevant in the cache. delete resolves into a Promise of true or false depending on the operation success.

That’s all about the CacheStorage API. Future of this API looks promising with support in major browsers such as Chrome, Edge, FireFox, and Opera. Safari also landed the CacheStorage API in Preview 43. Internet Explorer is the only one with a red flag for API.

For older browser that does not support Cache API, you can place a check
if(‘caches’ in window){/*use caches*/}

For offline web features, CacheStorage have evolved few patterns to handle different type of data based on priority are really handy to start with and should cover all general scenario. Since those features make usage of other APIs with CacheStorage , I’ve made a separate article Cache Recipes for that. Please head over to the next one after you are finished with this article.

With all that being said, serving fast and light cost data is way more important than it looks on 4g test devices and we as developers, are responsible to push all the native builtin superpowers of the awesome web platform to the end users and provide the best experience available. This contributes to our product, user experience and ultimately the Web Ecosystem❤️.
⚡⚡⚡#letsPushSuperPowers🔥🔥🔥

--

--

Ritik Rishu
Beginner's Guide to Mobile Web Development

Co-Founder @remotestate @remotexllp | All things #javascript | #bihari | #entrepreneur | #philanthropist wanna be