Offline First: An Intro to Service Workers

Peter Browne
LocalMed Engineering
4 min readFeb 26, 2016

Let’s assume our users will always have a fast, reliable internet connection.
– No One Ever

It seems ridiculous to say it out loud, but that’s how we’ve built websites and applications in the past. The goal of an “offline first” strategy is to assume the opposite: our users have a slow, unreliable internet connection. Why? Because network awareness is a good user experience in any context.

Unfortunately there is no magic bullet – if your application needs content from the server, at some point it will require a connection. But what happens when your user’s connection drops?

Enter the service worker…

What’s a Service Worker?

A generic entry point for persistent event-driven background processing in the Web Platform.
– Paraphrased from the W3C Spec

A service worker is essentially a background script for controlling web pages. This provides application authors the low-level control over the request lifecycle required to deliver a great user experience with low connectivity.

Service Worker Features

  • Intercept network requests
  • Requests caching
  • Push notifications
  • Background sync (proposed)
  • Geo-fencing (proposed)

Of which, intercepting and caching network requests are the essential building blocks an offline first application. Let’s look at how it works:

Service Worker Demos

These examples assume familiarity with:

Example 1: Service Worker Lifecycle

Demo on Github

Registering a Service Worker

Registering a service worker loads the script in the background and executes the initial lifecycle events. Notice the register method returns a Promise, and we can access a registration object for unregistering the worker.

The Service Worker Script

A basic service worker script is mainly comprised of event handlers. We’ll load this in Chrome and inspect the console:

Log output on first load

First impressions:

Log output on second load

Notice this time we see the fetch events, including URLs from another domain. The request object on fetch events is an instance of the Fetch Request class, with contains useful properties and methods for inspecting the outgoing request.

We also see stale logs from the previous load & an error, which can be very confusing. At this point, debugging service workers is difficult, and I think this is actually a bug in Chrome.

Example 2: Fetching

Demo on Github

Custom Responses from a Service Worker

Using the fetch event you can intercept the request and return a custom response. To do this, we pass a Response object to the respondWith method of the event.

When omitting a custom response, the default behavior is to fetch and return the original response. It’s exactly the same as explicitly calling event.respondWith(fetch(event.request)).

Please don’t do this…

Example 3: Prefetching & Caching

Demo on Github

In this example we will prefetch our assets and cache them for later requests.

Prefetching Assets

First off, there’s a global caches object which contains many Cache instances, keyed by unique cache names. The addAll convenience method handles fetching and storing the responses in the cache. We use this to prefetch the assets from CACHE_URLS.

Prefetching Network Log

In the network panel, we’ll see the requests for the assets occur in the service worker, which is indicated by the gear icon. This happens in the background and does not affect initial page load time.

Cache First Fetching Strategy

Next, in the fetch event we use a cache first strategy of:

  1. Attempt to get response from the cache, by matching the request.
  2. If there is no response, fallback to the default behavior of fetching.
Loading Assets From The Cache

When we load the next page, we’ll see the assets are loaded from the cache, indicated by the “(from ServiceWorker)” message.

And finally we’ll use the activate event to delete stale caches. We use the convention of incrementing the CACHE_VERSION for cache invalidation. This deletes any caches that do not match the current cache name.

Invalidating Stale Cache

Example 4: Offline Page

View Full Demo on Github

Now, let’s combine these techniques to create an offline page, which will indicate to users we’ve lost the connection.

Prefetching an Offline Page

We’ll use the same prefetching technique from Example 3 to cache the offline page on initial load.

Offline Page Fallback

This time, in the fetch event, we’ll use a fetch first strategy and fallback to a cached offline page. To be on the safe side, we check the request to ensure it’s a GET request for HTML content.

Our Offline Page In-action

It may not seem like much, but this is a great foundation for custom handling of the offline user experience. Here’s what we’ve accomplished:

  • Prefetched static assets, for faster subsequent page loads.
  • Added a fallback offline page when the user loses their connection.

--

--