Workbox: Add handlerDidError plugin callback

Created on 7 Jul 2020  路  1Comment  路  Source: GoogleChrome/workbox

Library Affected:
workbox-strategies

Issue or Feature Request Description:
workbox-routing currently provides "fallback" support via the setCatchHandler() method, which is usually written as a big switch statement with a bunch of different response strategies for different types of requests all bunched together.

In addition to this approach, which is implemented on the global-router level, it would be a good developer experience to expose Strategy-level fallback support, with a handler that gets triggered whenever the "main" handler for a Strategy results in a WorkboxError.

This new setCatchHandler method on the Strategy base class would also be associated with a new TypeScript callback signature, that matched the normal handlerCallback signature, but included an additional error property, in case the developer wants access to the underlying error. (The workbox-routing setCatchHandler() should also use this new callback signature, and pass the underlying error through there as well.)

You can approximate this now by creating your own handlerCallback with a try/catch block and a call to a Strategy inside of it, but that's more awkward than it needs to be. Code that's currently:

const networkFirst = new NetworkFirst({...});

const handlerWithFallback = async (params) => {
  try {
    // Hopefully you remember the await here, or catch will never be triggered!
    return await networkFirst.handle(params);
  } catch (error) {
    return caches.match('/offline');
  }
};

Could be rewritten as:

const networkFirst = new NetworkFirst({...});

networkFirst.setCatchHandler((params) => {
  // params.error has the underlying error, if needed.
  return caches.match('/offline');
});

I think it should be pretty clean to support this with the new Strategy base class that @philipwalton put in place for v6.

Discuss Feature Request workbox-strategies

Most helpful comment

After discussing this with @philipwalton a bit, a cleaner approach would be to create a new handlerDidError callback that gets invoked in between handlerWillStart and handlerWillRespond, when the _handle() method throws an error.

https://github.com/GoogleChrome/workbox/blob/b248ff887c3b84b2edc78131adb90f5005031885/packages/workbox-strategies/src/Strategy.ts#L170-L175

The signature would be roughly:

interface HandlerDidErrorCallbackParam {
  request: Request;
  event: ExtendableEvent;
  error: Error;
  state?: PluginState;
}

export interface HandlerDidErrorCallback {
  (param: HandlerDidErrorCallbackParam): Promise<Response | undefined>;
}

If the HandlerDidErrorCallback function throws or returns undefined, then the original error will be propagated and the handler will stop. If it returns a valid Response, then that will feed directly into the normal response flow, and handlerWillRespond will be invoked with the valid Response.

The example from the original proposal could be expressed as:

const networkFirst = new NetworkFirst({
  plugins: [{
    handlerDidError: () => caches.match('/offline'),
  }]
});

Using a handlerDidError callback makes it possible to create a new PrecacheFallbackPlugin in workbox-precaching that would implement handlerDidError by generating a response for a precached URL (the previous example would not work for URLs that include the __WB_REVISION=... query parameter, which many precached URLs would have). The common "fallback to a precached '/offline' error page" pattern would then be as simple as

registerRoute(
  ({request}) => request.mode === 'navigate',
  new NetworkOnly({
    plugins: [
      new PrecacheFallbackPlugin('/offline'),
    ],
  })
);

That should also be easy to translate into a runtimeCaching configuration, leading to an alternative to https://github.com/GoogleChrome/workbox/pull/2567

>All comments

After discussing this with @philipwalton a bit, a cleaner approach would be to create a new handlerDidError callback that gets invoked in between handlerWillStart and handlerWillRespond, when the _handle() method throws an error.

https://github.com/GoogleChrome/workbox/blob/b248ff887c3b84b2edc78131adb90f5005031885/packages/workbox-strategies/src/Strategy.ts#L170-L175

The signature would be roughly:

interface HandlerDidErrorCallbackParam {
  request: Request;
  event: ExtendableEvent;
  error: Error;
  state?: PluginState;
}

export interface HandlerDidErrorCallback {
  (param: HandlerDidErrorCallbackParam): Promise<Response | undefined>;
}

If the HandlerDidErrorCallback function throws or returns undefined, then the original error will be propagated and the handler will stop. If it returns a valid Response, then that will feed directly into the normal response flow, and handlerWillRespond will be invoked with the valid Response.

The example from the original proposal could be expressed as:

const networkFirst = new NetworkFirst({
  plugins: [{
    handlerDidError: () => caches.match('/offline'),
  }]
});

Using a handlerDidError callback makes it possible to create a new PrecacheFallbackPlugin in workbox-precaching that would implement handlerDidError by generating a response for a precached URL (the previous example would not work for URLs that include the __WB_REVISION=... query parameter, which many precached URLs would have). The common "fallback to a precached '/offline' error page" pattern would then be as simple as

registerRoute(
  ({request}) => request.mode === 'navigate',
  new NetworkOnly({
    plugins: [
      new PrecacheFallbackPlugin('/offline'),
    ],
  })
);

That should also be easy to translate into a runtimeCaching configuration, leading to an alternative to https://github.com/GoogleChrome/workbox/pull/2567

Was this page helpful?
0 / 5 - 0 ratings

Related issues

Sturgelose picture Sturgelose  路  3Comments

alexander-theijs picture alexander-theijs  路  3Comments

niveditaece picture niveditaece  路  3Comments

rafzan picture rafzan  路  3Comments

kaycebasques picture kaycebasques  路  4Comments