Just playing with some ideas
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.
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
.
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.
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.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.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);
data
- a FetchEvent
or FetchData
object.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.Router
is exposed on the global. Handlers are exposed as Router.handlers
, and handler types are exposed as Router.handlerTypes
.