# Service Worker Static Routing API

> How addRoutes() in the install event sends requests to the network, a cache or the fetch handler, which routes can skip worker startup, and when it rejects.

The Service Worker Static Routing API lets a service worker declare, at install time, which
requests the browser should serve from the network or from a cache without running the
worker to fetch them. The declaration is a list of rules passed to `event.addRoutes()` in
the `install` handler, and each rule pairs a condition on the request with the source the
response should come from.

## Syntax

```js
addRoutes(routerRules)
```

`routerRules` is a single rule object or an array of them, and the method returns a promise
that fulfills with `undefined`. The method is on `InstallEvent`, so it is reachable from the
`install` handler, and a rule set therefore belongs to the worker version that installed it.
The specification notes that `event.addRoutes(rules)` extends the lifetime of the event by
default, as if `event.waitUntil(promise)` had been called.

## Support by engine

BCD `api.InstallEvent.addRoutes` records support from Chrome 123 and Safari 27, mirrored
into the Chromium-based browsers and the Safari derivatives it lists, and no support in
Firefox, where it points at [Mozilla bug 1855580](https://bugzil.la/1855580) for the
implementation status. Chrome's launch post describes the API as available from Chrome 123
after an origin trial that began in Chrome 116 under the name `registerRouter()`, a method
the post marks deprecated with removal planned for Chrome 125.

## Parameters

A rule is `{ condition, source }`. The condition keys below are the ones MDN's
[`addRoutes()` reference](https://developer.mozilla.org/en-US/docs/Web/API/InstallEvent/addRoutes)
documents; when several are present in one condition, a resource must meet all of them to
match the rule.

| Condition key | Value | Matches |
|---|---|---|
| `urlPattern` | a [`URLPattern`](https://developer.mozilla.org/en-US/docs/Web/API/URLPattern) instance, or a constructor input pattern | the URLs that match the rule. Regular expression capturing groups are not allowed, so `URLPattern.hasRegExpGroups` has to be `false`. |
| `requestMethod` | string, such as `"get"`, `"put"` or `"head"` | the HTTP method the request is sent by. |
| `requestMode` | string, such as `"same-origin"`, `"no-cors"` or `"cors"` | the mode the request has. |
| `requestDestination` | string, such as `"audio"`, `"document"`, `"script"` or `"worker"` | the destination of the request, that is, the content type requested. |
| `runningStatus` | `"running"` or `"not-running"` | the required running status of the service worker. |
| `or` | an array of condition objects | one set of the nested conditions. Conditions inside `or` are mutually exclusive with other conditions. |
| `not` | a condition object | conditions that must explicitly not be met. Conditions inside `not` are mutually exclusive with other conditions. |

`source` is an enumerated value or an object. MDN's enumerated values are `"cache"`,
`"network"`, `"fetch-event"` and `"race-network-and-fetch-handler"`, the last of which
attempts the network and the fetch event handler simultaneously and uses whichever
completes first. MDN pairs `"fetch-event"` with the `runningStatus` condition to load a
resource through the worker while it is running and fall back to a static route on the
network while it is not. The object form carries a single `cacheName` property naming a
cache; MDN notes that if that cache does not exist the browser defaults to the network so
the requested resources can still be obtained when the network is available.

## Exceptions

MDN lists these causes for a `TypeError`: a rule object inside `routerRules` that is
invalid, a `source` of `"fetch-event"` on a worker with no fetch event handler, and `or`
combined with another condition type.

- An invalid rule rejects the call that carried it, so validate a generated rule list before
  shipping it.
- `"fetch-event"` and `"race-network-and-fetch-handler"` depend on a registered `fetch`
  handler. The
  [`addRoutes()` algorithm](https://w3c.github.io/ServiceWorker/#register-router-method)
  returns a promise rejected with a `TypeError` when `source` is either `"fetch-event"` or
  `"race-network-and-fetch-handler"` and the service worker's set of event types to handle
  does not contain `fetch`. Rules whose `source` is `"network"`, `"cache"` or the
  `cacheName` object are not named by that step.
- A condition that carries `or` next to a sibling key has to be rewritten as two rules, one
  per branch, each repeating the sibling. MDN's own example of that rewrite pairs
  `urlPattern` with `requestMethod` twice.
- The specification also rejects the promise with a `TypeError` when the Check Router
  Registration Limit algorithm, run over a copy of the worker's existing list of router
  rules with the new rules appended, returns false.

:::observed
Chromium's rule converter, `third_party/blink/renderer/modules/service_worker/service_worker_router_type_converter.cc`,
throws its `TypeError`s with these exact strings
([Chromium source](https://chromium.googlesource.com/chromium/src/+/main/third_party/blink/renderer/modules/service_worker/service_worker_router_type_converter.cc)):

```text
At least one condition must be set, but no condition has been set to the rule.
Cannot set other conditions when the `or` condition is specified
Cannot set other conditions when the `not` condition is specified
fetch-event source is specified without a fetch handler
race-network-and-fetch-event source is specified without a fetch handler
```

The message for the racing source names it `race-network-and-fetch-event`, while the
`source` value a rule sets is `race-network-and-fetch-handler`.
:::

An empty condition object is a rejection rather than a rule matching everything, which is
the first string above.

## Examples

The examples below are the shapes most apps need: bypass the worker on a path, declare
routes where the method exists, and serve image extensions from a named cache.

### Sending a path to the network when the worker is cold

The condition combines a URL pattern with `runningStatus`, so the rule applies while the
service worker is not running, which is the case that costs a start-up.

```js
addEventListener('install', (event) => {
  event.addRoutes({
    condition: {
      urlPattern: '/articles/*',
      runningStatus: 'not-running',
    },
    source: 'network',
  });
});
```

While the worker is running this rule does not match, and the request reaches the `fetch`
handler, so a cache-first strategy for the same path still applies on warm navigations.
Chrome's launch post shows the mirror-image rule, `runningStatus: "running"` with
`source: "fetch-event"`.

### Declaring routes where the method exists, with a fallback

`addRoutes()` is a method on the event object, so it can be tested before use. The absent
branch is not a degraded mode: it is the behaviour a service worker had before static
routes, where the `fetch` handler decides everything.

```js
addEventListener('install', (event) => {
  if (!('addRoutes' in event)) {
    // No addRoutes() on this event: leave the routing to the fetch handler.
    return;
  }
  event
    .addRoutes([
      { condition: { urlPattern: '/form/*', requestMethod: 'post' }, source: 'network' },
      { condition: { requestDestination: 'font' }, source: { cacheName: 'fonts' } },
    ])
    .catch((error) => {
      console.error('static routes rejected, using the fetch handler instead', error);
    });
});
```

Keep the `catch`. The method reports its failures by rejecting rather than throwing
synchronously, so a rejection with no handler leaves the app running on the fetch handler
alone and surfaces as an unhandled promise rejection.

### Serving two image extensions from one named cache

`or` takes a list of conditions and is mutually exclusive with other conditions, so the
extensions go inside the array and the shared source stays outside it.

```js
addEventListener('install', (event) => {
  event.addRoutes({
    condition: {
      or: [{ urlPattern: '*.png' }, { urlPattern: '*.jpg' }],
    },
    source: { cacheName: 'pictures' },
  });
});
```

To narrow the same two patterns to `GET` requests, write two rules, each with its own
`urlPattern` and `requestMethod`, which is the rewrite MDN gives for this case.

Chrome DevTools shows what the browser registered: Chrome's launch post states that
registered router rules are displayed in the Service Worker tab of the Application panel,
and that in the Network panel a request matching a registered rule is indicated in the size
column, with the registered router ID shown on pointer hover.

## See also

- [FetchEvent and request routing](/reference/service-worker/fetch-event/)
- [Caching strategies for service workers](/reference/service-worker/caching-strategies/)
- [NavigationPreloadManager](/reference/service-worker/navigation-preload/)
- [Service worker lifecycle](/reference/service-worker/lifecycle/)
- [InstallEvent: addRoutes() method](https://developer.mozilla.org/en-US/docs/Web/API/InstallEvent/addRoutes) (developer.mozilla.org)
- [Service Workers: the addRoutes() method](https://w3c.github.io/ServiceWorker/#register-router-method) (w3c.github.io)
- [Use the Service Worker Static Routing API to bypass the service worker for specific paths](https://developer.chrome.com/blog/service-worker-static-routing) (developer.chrome.com)