Passing state info into a service worker before `install`

Brett Zamir picture Brett Zamir · Jun 8, 2017 · Viewed 9.1k times · Source

Background

I'm new to service workers but working on a library that is intended to become "offline-first" (really, almost "offline-only") (FWIW, the intent is to allow consumers of the library to provide JSON config representing tabular multilinear texts and get in return an app which allows their users to browse these texts in a highly customizable manner by paragraph/verse ranges.)

Other projects are to install the library as a dependency and then supply information via our JavaScript API such as the path of a JSON config file indicating the files that our app will consume to produce an (offline) app for them.

While I know we could do any of the following:

  1. require users provide a hard-coded path from which our service worker's install script could use waitUntil with its own JSON request to retrieve the user's necessary files
  2. skip the service worker's install step of the service worker for the JSON file, and rely on fetch events to update the cache, providing a fallback display if the user completed the install and went offline before the fetches could occur.
  3. Post some state info from our main script to a server which the service worker, once registered, would query before completing its install event.

...but all choices seems less than ideal because, respectively:

  1. Our library's consumers may prefer to be able to designate their own location for their JSON config.
  2. Given that the JSON config designates files critical to showing their users anything useful, I'd rather not allow an install to complete only to say that the user has to go back online to get the rest of the files if they were not able to remain online after the install event to see all the required fetches occur.
  3. Besides wanting to avoid more trips to the server and extra code, I'd prefer for our code to be so offline-oriented as to be able to work entirely on mere static file servers.

Question:

Is there some way to pass a message or state information into a service worker before the install event occurs, whether as part of the query string of the service worker URL, or through a messaging event? The messaging event could even technically arrive after the install event begins as long as it can occur before a waitUntil within the install is complete.

I know I could test this myself, but I'd like to know what best practices might be anyways when the critical app files must themselves be dynamically obtained as in such libraries as ours.

I'm guessing indexedDB might be the sole alternative here (i.e., saving the config info or path of the JSON config to indexedDB, registering a service worker, and retrieving the indexedDB data from within the install event)? Even this would not be ideal as I'm letting users define a namespace for their storage, but I need a way for it too to be passed into the worker, or otherwise, multiple such apps on the origin could clash.

Answer

Jeff Posnick picture Jeff Posnick · Jun 8, 2017

Using a Query Parameter

If you find it useful, then yes, you can provide state during service worker installation by including a query parameter to your service worker when you register it, like so:

// Inside your main page:
const pathToJson = '/path/to/file.json';
const swUrl = '/sw.js?pathToJson=' + encodeURIComponent(pathToJson);
navigator.serviceWorker.register(swUrl);

// Inside your sw.js:
self.addEventListener('install', event => {
  const pathToJson = new URL(location).searchParams.get('pathToJson');
  event.waitUntil(
    fetch(pathToJson)
      .then(response => response.json())
      .then(jsonData => /* Do something with jsonData */)
  );
});

A few things to note about this approach:

  • If you fetch() the JSON file in your install handler (as in the code sample), that will effectively happen once per version of your service worker script (sw.js). If the contents of the JSON file change, but everything else stays the same, the service worker won't automatically detect that and repopulate your caches.

  • Following from the first point, if you work around that by, e.g., including hash-based versioning in your JSON file's URL, each time you change that URL, you'll end up installing a new service worker. This isn't a bad thing, per se, but you need to keep it in mind if you have logic in your web app that listens for service worker lifecycle events.

Alternative Approaches

You also might find it easier to just add files to your caches from within the context of your main page, since browsers that support the Cache Storage API expose it via window.caches. Precaching the files within the install handler of a service worker does have the advantage of ensuring that all the files have been cached successfully before the service worker installs, though.

Another approach is to write the state information to IndexedDB from the window context, and then read from IndexedDB inside of your service worker's install handler.