The #ChromeDevSummit site is live, happening Nov 12-13 in San Francisco, CA
Check it out for details and request an invite. We'll be diving deep into modern web tech & looking ahead to the platform's future.

Workbox Broadcast Cache Update

What is Broadcast Cache Update?

When responding to requests with cached entries, while being fast, it comes with a tradeoff that users may end up seeing stale data.

The workbox-broadcast-cache-update module provides a standard way of notifying Window Clients that a cached response has been updated. This is most commonly used along with the staleWhileRevalidate strategy.

Whenever the "revalidate" step of that strategy retrieves a response from the network that differs from what was previously cached, this module will use the Broadcast Channel API to announce the update. Clients can listen for updates using that API and take appropriate action, like automatically displaying a message to the user letting them know that updates are available.

How are updates determined?

Certain headers of the cached and new Response objects are compared, and if any of the headers have different values, it's considered an update.

By default, the Content-Length, ETag, and Last-Modified headers are compared.

Workbox uses header values instead of a byte-for-byte comparison of response bodies to be more efficient, in particular for potentially large responses

Using Broadcast Cache Update

The library is intended to be used along with the staleWhileRevalidate caching strategy, since that strategy involves returning a cached response immediately, but also provides a mechanism for updating the cache asynchronously.

To broadcast updates, you just need to add a broadcastUpdate.Plugin to your strategy options.

workbox.routing.registerRoute(
  new RegExp('/api/'),
  workbox.strategies.staleWhileRevalidate({
    plugins: [
      new workbox.broadcastUpdate.Plugin('api-updates')
    ]
  })
);

This will broadcast messages on the channel 'api-updates', but you should customize it to something relevant to your app.

In your web app, you can listen for these events like so:

const updatesChannel = new BroadcastChannel('api-updates');
updatesChannel.addEventListener('message', async (event) => {
  const {cacheName, updatedUrl} = event.data.payload;

  // Do something with cacheName and updatedUrl.
  // For example, get the cached content and update
  // the content on the page.
  const cache = await caches.open(cacheName);
  const updatedResponse = await cache.match(updatedUrl);
  const updatedText = await updatedResponse.text();
  ...
});

Message format

When a message event listener as received in your web app, the event.data property will have the following format:

{
  type: 'CACHE_UPDATED',
  meta: 'workbox-broadcast-cache-update',
  // The two payload values vary depending on the actual update:
  payload: {
    cacheName: 'the-cache-name',
    updatedUrl: 'https://example.com/'
  }
}

Customize Headers to Check

You can customize the headers to check by setting the headersToCheck property.

workbox.routing.registerRoute(
  new RegExp('/api/'),
  workbox.strategies.staleWhileRevalidate({
    plugins: [
      new workbox.broadcastUpdate.Plugin(
        'api-updates',
        headersToCheck: ['X-My-Custom-Header']
      )
    ]
  })
);

Advanced Usage

While most developers will use workbox-broadcast-cache-update as a plugin of a particular strategy as shown above, it's possible to use the underlying logic in service worker code.

const broadcastUpdate = new workbox.broadcastUpdate.BroadcastCacheUpdate(
  'api-updates',
  {
    headersToCheck: ['X-My-Custom-Header'],
  }
);

const cacheName = 'api-cache';
const url = 'https://example.com/api';

const cache = await caches.open(cacheName);
const previousResponse = await cache.match(url);

const networkResponse = await fetch(url);

broadcastUpdate.notifyIfUpdated(
  previousResponse,
  networkResponse,
  url,
  cacheName
);