Skip to main content

Routing

Original source (Apache 2.0 License). Adapted for Serwist's usage.

Introduction

A service worker can intercept network requests for a page. It may respond to the browser with cached content, content from the network or content generated in the service worker.

Routing is how Serwist allows different functions to handle different requests.

When a network request is intercepted, Serwist attempts to respond to the request using the supplied routes and handlers.

Routing diagram

The main things to note from the above are:

  • The method of a request is important. By default, routes are registered for GET requests. If you wish to intercept other types of requests, you'll need to specify the method.
  • The register order is important. If multiple registered routes can handle a request, the route that is registered first will be used to respond to the request.

Applying caching strategies with route matching

The Serwist class exposes the registerCapture method to allow you to match a pattern of routes to a caching strategy. registerCapture accepts two arguments:

  • A string, regular expression, or a match callback to specify route matching criteria.
  • A handler for the route — typically a built-in Strategy.
You may also use the runtimeCaching option of Serwist's constructor. It is essentially a syntactic sugar for registerCapture.

When the service worker intercepts a network request, Serwist tries to match the URL of the request to one of the registered handlers, which will then be used to generate a response. In the following example, we register a route that matches incoming same-origin image requests, applying the CacheFirst strategy.

import { CacheFirst, Serwist } from "serwist";

const serwist = new Serwist();

serwist.registerCapture(({ request, sameOrigin }) => {
  return sameOrigin && request.destination === "image";
}, new CacheFirst());

serwist.addEventListeners();

Matching all navigation requests

NavigationRoute allows you to match all navigation requests. If your site is a single page application, you can use NavigationRoute to return a specific response for all navigation requests:

// Example: handling all navigations using an app shell.
import { NavigationRoute, Serwist } from "serwist";

const serwist = new Serwist({
  // Assuming that your precache list includes "/app-shell.html".
  precacheEntries: self.__SW_MANIFEST,
});

// This assumes that "/app-shell.html" has been precached.
serwist.registerRoute(new NavigationRoute(serwist.createHandlerBoundToUrl("/app-shell.html")));

serwist.addEventListeners();

Alternatively, you can use precacheOptions.navigateFallback:

import { NavigationRoute, Serwist } from "serwist";

const serwist = new Serwist({
  // Assuming that your precache list includes "/app-shell.html".
  precacheEntries: self.__SW_MANIFEST,
  precacheOptions: {
    navigateFallback: "/app-shell.html",
  },
});

serwist.addEventListeners();

Setting a default handler

If you want to supply a handler for requests that don't match any route at all, you can set a default handler.

import { CacheFirst } from "serwist";

const cacheFirst = new CacheFirst();

// Example: If HTTP method is `GET`, use the `CacheFirst` strategy,
// go to the network otherwise.
serwist.setDefaultHandler(({ event, request, url, params }) => {
  if (request.method === "GET") {
    return cacheFirst.handle({ event, request, url, params });
  }
  return fetch(request);
});

serwist.addEventListeners();

Setting a catch handler

In the case of any of your routes throwing an error, you can capture and degrade gracefully by setting a catch handler.

// Example: Setting up offline fallbacks.
serwist.setCatchHandler(async ({ request }) => {
  const dest = request.destination;

  if (dest === "document") {
    const match = await serwist.matchPrecache("/offline.html");
    return match || Response.error();
  }

  if (dest === "image") {
    const match = await serwist.matchPrecache("/fallback.png");
    return match || Response.error();
  }

  if (dest === "font") {
    const match = await serwist.matchPrecache("/fonts/fallback.woff2");
    return match || Response.error();
  }

  return Response.error();
});

Defining a route for non-GET requests

All routes by default are assumed to be for GET requests.

If you would like to route requests of other HTTP methods, you need to specify the method when registering the route like so:

import { NetworkFirst } from "serwist";

serwist.registerCapture(/^\/api\/.*/, new NetworkFirst(), "POST");

serwist.addEventListeners();