Cache API

By  on  

The awesome ServiceWorker API is meant to give developers a bit more control over what is and isn't cached, and how.  Sure we can play games with ETags and the like but doing it programmatically with JavaScript just feels better, more controllable.  As with every API, however, adding stuff to cache isn't just fun and games -- you have to do the cleanup work yourself too, and by "cleanup work" I mean having to delete cache.

Let's have a look at how you can get caches, add and delete requests from caches, and how you can then delete an entire cache!

Detecting the cache API

To detect if the browser supports the Cache API...

if('caches' in window) {
  // Has support!
}

...check for the presence of the caches object within the window.

Creating a Cache

Creating a cache requires a caches.open call with a cache name:

caches.open('test-cache').then(function(cache) {
  // Cache is created and accessible
});

The caches.open call returns a Promise and the cache object that was either created or existed before the open call.

Adding to Cache

You can think of a cache as an array of Request objects which act as keys to their responses which are stored by the browser. Simple addition to cache is happens via two main methods: add and addAll. You can provide these methods a string for the URL that should be fetched and cached or a Request object. You can learn more about Request objects by reading my fetch API post.

To batch add URLs to cache, you can use the addAll method:

caches.open('test-cache').then(function(cache) { 
  cache.addAll(['/', '/images/logo.png'])
    .then(function() { 
      // Cached!
    });
});

The addAll method accepts an array of URLs whose URLs and responses should be cached. addAll returns a Promise. To add a single URL, use the add method:

caches.open('test-cache').then(function(cache) {
  cache.add('/page/1');  // "/page/1" URL will be fetched and cached!
});

add can also accept a custom Request:

caches.open('test-cache').then(function(cache) {
  cache.add(new Request('/page/1', { /* request options */ }));
});

Similar to add is put whereby you can add a URL and its Response object:

fetch('/page/1').then(function(response) {
  return caches.open('test-cache').then(function(cache) {
    return cache.put('/page/1', response);
  });
});

Exploring Cache

To view requests that have already been cached, you can use the individual cache's keys method to retrieve an array of cached Request objects:

caches.open('test-cache').then(function(cache) { 
  cache.keys().then(function(cachedRequests) { 
    console.log(cachedRequests); // [Request, Request]
  });
});

/*
Request {
  bodyUsed: false
  credentials: "omit"
  headers: Headers
  integrity: ""
  method: "GET"
  mode: "no-cors"
  redirect: "follow"
  referrer: ""
  url: "https://fullhost.tld/images/logo.png"
}
*/

If you'd like to view the response of a cached Request, you can do so by using cache.match or cache.matchAll:

caches.open('test-cache').then(function(cache) {
  cache.match('/page/1').then(function(matchedResponse) {
    console.log(matchedResponse);
  });
});

/*
Response {
  body: (...),
  bodyUsed: false,
  headers: Headers,
  ok: true,
  status: 200,
  statusText: "OK",
  type: "basic",
  url: "https://davidwalsh.name/page/1"
}
*/

You can learn more about Response objects by reading my fetch API post.

Removing a Cached Request

To remove a request from cache, use the cache's delete method:

caches.open('test-cache').then(function(cache) {
  cache.delete('/page/1');
});

The cache will no longer contain /page/1!

Getting Existing Cache Names

To get the names of existing caches, use caches.keys:

caches.keys().then(function(cacheKeys) { 
  console.log(cacheKeys); // ex: ["test-cache"]
});

window.caches.keys() again returns a promise.

Deleting A Cache

Deleting a cache is simple once you have cache key name:

caches.delete('test-cache').then(function() { 
  console.log('Cache successfully deleted!'); 
});

You'll often delete a cache when you're replacing with a new one (to trigger re-installation of a new service worker):

// Assuming `CACHE_NAME` is the newest name
// Time to clean up the old!
var CACHE_NAME = 'version-8';

// ...

caches.keys().then(function(cacheNames) {
  return Promise.all(
    cacheNames.map(function(cacheName) {
      if(cacheName != CACHE_NAME) {
        return caches.delete(cacheName);
      }
    })
  );
});

You'll want to keep these snippets in your toolbox for when you look to become a service worker expert.  Chrome and Firefox now have service worker support so you'll be seeing many more sites/apps available (reliably) offline and loading at much faster rates.  Enjoy!

Recent Features

  • By
    CSS @supports

    Feature detection via JavaScript is a client side best practice and for all the right reasons, but unfortunately that same functionality hasn't been available within CSS.  What we end up doing is repeating the same properties multiple times with each browser prefix.  Yuck.  Another thing we...

  • By
    Write Simple, Elegant and Maintainable Media Queries with Sass

    I spent a few months experimenting with different approaches for writing simple, elegant and maintainable media queries with Sass. Each solution had something that I really liked, but I couldn't find one that covered everything I needed to do, so I ventured into creating my...

Incredible Demos

  • By
    Select Dropdowns, MooTools, and CSS Print

    I know I've harped on this over and over again but it's important to enhance pages for print. You can do some things using simple CSS but today's post features MooTools and jQuery. We'll be taking the options of a SELECT element and generating...

  • By
    MooTools Font-Size Scroller with Cookie Save

    Providing users as many preferences as possible always puts a smile on the user's face. One of those important preferences is font size. I can see fine but the next guy may have difficulty with the font size I choose. That's why...

Discussion

  1. Does not this mechanism keep time when a url is added to cache? How can I know when I should update a url?
    For example I need to cache urls for 1 hour. Do I need to keep somewhere else the time when I add url to cache?

    • The expires time should be on the server; this API stores the response provided to it.

    • So sad :(
      I don’t have access to the server – I’m writing a browser extension. And that server doesn’t send cache headers. I guess I’ll do it using IndexedDB.

  2. Bruno Giubilei

    it’s possible to cache a image from another location?

  3. Steve

    Great post! How would this sort of caching work for AJAX rich pages with several AJAX calls?

  4. Kamila

    I can’t create new cashe and i get an error that cashe is not defined – why???

  5. Ranjana

    I am looking to access the cached data of chrome using javascript. Any help?

    • Ranjana

      I tried creating a cache, added an url, and looking to view response of cached request. Response returned is an object of ReadableStream. How to parse readable stream to get actual response?

    • Chris

      You need to parse the Response object. If this is JSON you can do e.g.

      cache.match(url).then(result => result.json())

      or potentially for an image:

      cache.match(url).then(result => result.blob())

Wrap your code in <pre class="{language}"></pre> tags, link to a GitHub gist, JSFiddle fiddle, or CodePen pen to embed!