sw-routes

Just playing with some ideas

Stars
41

sw-routes

A basic router for service worker fetch events.

npm install sw-routes

This library is designed to be used with an ES6-aware bundler like Webpack or Rollup, but there's also a prebuilt version.

Guide

Creating a router

import Router from 'sw-routes';

const router = new Router();

// Add your routes

router.addFetchListener();

If you don't want sw-routes to write the fetch listener for you, see router.handle.

Adding routes

Routes are created using:

router.get(path, ...handlers);
// or
router.get(...handlers);

.get only handles GET requests. There is also .post, .put, .delete for other HTTP methods, and .all for all methods.

Paths can contain regex & patterns.

In their simplest form, handlers are just functions:

router.get('/whatever/', async fetchData => {
  try {
    const response = await fetch(fetchData.request);
    if (!response.ok) throw Error('Not ok');
    return response;
  }
  catch (err) {
    return caches.match('/error-page');
  }
});

Functions are "request handlers", and are called in sequence until one provides a response, or a promise for a response (as above).

For details on fetchData, see the API docs.

Handlers

sw-routes provides many common handlers. For instance, if you wanted to respond from the cache, falling back to the network:

import { fromCache, fromNetwork } from 'sw-routes/handlers';

router.get(fromCache(), fromNetwork());

If you want to respond from the network, falling back to the cache, just flip the handlers around:

router.get(fromNetwork(), fromCache());

Here are all the handlers sw-routes provides:

  • fromCache Try to get a response from a cache. It can be a specific item, or one that matches the current request.
  • fromNetwork Try to get a response from the network.
  • fromPreload Use any preloaded response.
  • ifMethod Only run subsequent handlers if the request uses a particular HTTP method. This is what .get etc use internally.
  • ifNavigation Only run subsequent handlers if the request is a page navigation.
  • ifUrl Only run subsequent handlers if the request matches a particular URL. If you pass a string as the first argument to .get etc, it's passed to this.
  • race Race a number of handlers. First one to provide a response wins.
  • sequence Shortcut for creating a sub-router.
  • toCache Add the response to a cache (unless it came from the cache).
  • updateCache Add the response to a cache. If it came from the cache, fetch a fresh copy instead.
  • wait Add an artificial delay. This is useful for creating timeouts.

Writing your own handler

When creating handlers, bare functions are "request handlers", but there are other types, such as "conditional handlers":

import { conditionalHandler } from 'sw-routers/handler-types';

router.get(conditionalHandler(data => {
  return data.request.mode == 'no-cors';
}), fromNetwork());

Here are all the types:

  • requestHandler Called if a response hasn't been provided yet. Can return a response.
  • responseHandler Called if a response has been provided. Can return a different response.
  • anyHandler Called whether a response has been provided or not. Can return a response.
  • responseWaitUntilHandler Called if a response has been provided. Runs asynchronously. Useful for doing something with the response such as caching.
  • waitUntilHandler Called whether a response has been provided or not. Runs asynchronously. Useful for doing bookkeeping & clean-up work.
  • conditionalHandler Skips subsequent handlers in the sequence if it returns false.
  • errorHandler Called if a previous handler threw an error or rejected. Can return a response.

API

Router

import Router from 'sw-routes';
const router = new Router();

router.all/get/post/put/delete

Create and add a sub-router with handlers.

router.get(urlPattern, ...items);
router.get(...items);
  • urlPattern: Becomes a conditional handler using ifUrl.
  • items: One or more handlers or sub-routers.

all handles requests of all HTTP methods, whereas get etc handle requests of specific HTTP methods.

router.add

Add items to the current router. This is useful for adding catch-all handlers.

router.add(...items);
  • items: One or more handlers.

router.addFetchListener

Adds a fetch listener which uses the router to handle fetches.

router.addFetchListener();

router.handle

Process a fetch event and return a response.

const response = await router.handle(data);

This is useful if you need to add your own fetch event logic around the router:

addEventListener('fetch', event => {
  // Your code goes here.
  event.respondWith(async function() {
    // Maybe more of your own code?
    const response = await router.handle(event);
    // Some more of your own code, perhaps?
    return response; // or maybe something else!
  }());
});

FetchData

An instance of FetchData is given to each handler.

Its has the following properties from the fetch event.

  • request
  • preloadResponse
  • clientId
  • waitUntil

But also:

  • url: A parsed version of request's URL.
  • error: The error thrown by a previous handler.
  • response: The response provided by a previous handler.
  • params: Params from the URL match.

Prebuilt version

dist/index.js.

Router is exposed on the global. Handlers are exposed as Router.handlers, and handler types are exposed as Router.handlerTypes.