How To Invalidate Service Worker Cache On Every Update?

How To Invalidate Service Worker Cache On Every Update?

·

3 min read

Service workers are really powerful and are being used for offline access, push notifications and a bunch of other things on today's web.

Service workers really deserve a massive in-depth blog which I will be working on for sure.

This series of problems and solutions is meant to document the problems I faced and the solutions which I figured out, thus, helping other developers.

So, let us start with a basic problem which I faced related to service workers.

The Problem

Say your website is being accessed by a lot of users daily due to which your website will be cached by the service worker on all of their browsers. Now, if you deploy a new update on your website, you have to invalidate the cache stored in the browsers of all of your users.

If you don't invalidate the cache, your users will always be accessing the cached version i.e old version only.

This is a big problem!!

Let us have a look at the service worker code i.e the javascript code which is responsible for installing the service worker on a users browser.

Service Worker Code

var CACHE_NAME = "pwa-v1";
//Just a sample name, the cache name should be more relatable to the application
var urlsToCache = ["/", "/login", "/register"];

// Install a service worker
self.addEventListener("install", (event) => {
  // Perform install steps
  caches.open(CACHE_NAME).then(function (cache) {
    Promise.all(
      urlsToCache.map(function (url) {
        cache.add(url);
      })
    );
  });
});

// Cache lookup and fetch the request
self.addEventListener("fetch", (event) => {
  event.respondWith(
    caches.match(event.request).then(function (response) {
      // Cache hit - return response
      if (response) {
        return response;
      }
      return fetch(event.request).then(function (response) {
        if (!response || response.status !== 200 || response.type !== "basic") {
          return response;
        }

        //Clone the response before putting into cache so that response to browser and response to cache happens in two difference streams
        var responseForCache = response.clone();
        caches.open(CACHE_NAME).then(function (cache) {
          cache.put(event.request, responseForCache);
        });
        return response;
      });
    })
  );
});

// Update a service worker
self.addEventListener("activate", (event) => {
  var cacheWhitelist = ["pwa-v1"];
  event.waitUntil(
    caches.keys().then((cacheNames) => {
      return Promise.all(
        cacheNames.map((cacheName) => {
          if (cacheWhitelist.indexOf(cacheName) === -1) {
            return caches.delete(cacheName);
          }
        })
      );
    })
  );
});

If you haven't worked with service workers and want to understand this code, here is a fantastic resource by google: Service Workers: an Introduction

Solution

Coming to the solution, It has a really simple solution.

You just change the CACHE_NAME and the cacheWhitelist array.

For example:

Before update

CACHE_NAME = "pwa-v1"
cacheWhitelist = ["pwa-v1"]

Before pushing the updated code, change these two in your worker.js to

CACHE_NAME = "pwa-v2"
cacheWhitelist = ["pwa-v2"]

Like that on every update, you can keep changing these and the previous cache on your user's website will be deleted and the new version will be stored in the new cache.

Working of the Solution

After deploying the updated service worker, whenever someone accesses your website, the browser will get the new service worker file.

As the browser observes that this service worker is different, it installs the new service worker but nothing happens in that session.

Once the user closes the website, then the browser activates the new service worker and caches a new version of the website.

This event runs the activate event listener which deletes all the old caches.

So, that is how the whole update process works!!

Let me know if you have any doubts in the comments and you can always ping me on Twitter as well.

Follow me on Twitter @saai_tejaa