When you send data to a web server, sometimes the requests will fail. It
may be because the user has lost connectivity, or it may be because the
server is down; in either case you often want to try sending the requests
again later.

The new [BackgroundSync API](https://wicg.github.io/BackgroundSync/spec/)
is an ideal solution to this problem. When a service worker detects that a
network request has failed, it can register to receive a `sync` event,
which gets delivered when the browser thinks connectivity has returned.
Note that the sync event can be delivered *even if the user has left the
application*, making it much more effective than the traditional method of
retrying failed requests.

Workbox Background Sync is designed to make it easier to use the
BackgroundSync API and integrate its usage with other Workbox modules. It
also implements a fallback strategy for browsers that don't yet implement
BackgroundSync.

Browsers that support the BackgroundSync API will automatically replay failed
requests on your behalf at an
[interval managed by the browser](https://developer.chrome.com/blog/background-sync#how_to_request_a_background_sync),
likely using exponential backoff between replay attempts. In browsers that
don't natively support the BackgroundSync API, Workbox Background Sync will
automatically attempt a replay whenever your service worker starts up.

## Basic Usage

The easiest way to use Background Sync is to use the `Plugin` that will
automatically Queue up failed requests and retry them when future `sync`
events are fired.

    import {BackgroundSyncPlugin} from 'workbox-background-sync';
    import {registerRoute} from 'workbox-routing';
    import {NetworkOnly} from 'workbox-strategies';

    const bgSyncPlugin = new BackgroundSyncPlugin('myQueueName', {
      maxRetentionTime: 24 * 60, // Retry for max of 24 Hours (specified in minutes)
    });

    registerRoute(
      /\/api\/.*\/*.json/,
      new NetworkOnly({
        plugins: [bgSyncPlugin],
      }),
      'POST'
    );

`BackgroundSyncPlugin` hooks into the
[`fetchDidFail` plugin callback](https://developer.chrome.com/docs/workbox/using-plugins), and
`fetchDidFail` is only invoked if there's an exception thrown, most likely due
to a network failure. This means that requests won't be retried if there's a
response received with a
[`4xx` or `5xx` error status](https://developer.mozilla.org/docs/Web/HTTP/Status).
If you would like to retry all requests that result in, e.g., a `5xx` status,
you can do so by
[adding a `fetchDidSucceed` plugin](https://github.com/GoogleChrome/workbox/issues/2599#issuecomment-900304969)
to your strategy:

    const statusPlugin = {
      fetchDidSucceed: ({response}) => {
        if (response.status >= 500) {
          // Throwing anything here will trigger fetchDidFail.
          throw new Error('Server error.');
        }
        // If it's not 5xx, use the response as-is.
        return response;
      },
    };

    // Add statusPlugin to the plugins array in your strategy.

## Advanced Usage

Workbox Background Sync also provides a `Queue` class, which you can
instantiate and add failed requests to. The failed requests are stored
in [IndexedDB](https://developer.mozilla.org/docs/Web/API/IndexedDB_API)
and are retried when the browser thinks connectivity is restored (i.e.
when it receives the sync event).

### Creating a Queue

To create a Workbox Background Sync Queue you need to construct it with
a queue name (which must be unique to your
[origin](https://developer.mozilla.org/docs/Web/Security/Same-origin_policy#Definition_of_an_origin)):

    import {Queue} from 'workbox-background-sync';

    const queue = new Queue('myQueueName');

The queue name is used as part of the tag name that gets
[`register()`](https://wicg.github.io/BackgroundSync/spec/#dom-syncmanager-register)-ed
by the global
[`SyncManager`](https://wicg.github.io/BackgroundSync/spec/#sync-manager-interface). It's
also used as the
[Object Store](https://developer.mozilla.org/docs/Web/API/IDBObjectStore) name for
the IndexedDB database.

> [!NOTE]
> **Note:**It's not important that you know the details above, but they're the reason the queue name has to be unique to your origin.

### Adding a request to the Queue

Once you've created your Queue instance, you can add failed requests to it.
You add failed request by invoking the `.pushRequest()` method. For example,
the following code catches any requests that fail and adds them to the queue:

    import {Queue} from 'workbox-background-sync';

    const queue = new Queue('myQueueName');

    self.addEventListener('fetch', event => {
      // Add in your own criteria here to return early if this
      // isn't a request that should use background sync.
      if (event.request.method !== 'POST') {
        return;
      }

      const bgSyncLogic = async () => {
        try {
          const response = await fetch(event.request.clone());
          return response;
        } catch (error) {
          await queue.pushRequest({request: event.request});
          return error;
        }
      };

      event.respondWith(bgSyncLogic());
    });

Once added to the queue, the request is automatically retried when the
service worker receives the `sync` event (which happens when the browser
thinks connectivity is restored). Browsers that don't support the
BackgroundSync API will retry the queue every time the service worker is
started up. This requires the page controlling the service worker to be
running, so it won't be quite as effective.

## Testing Workbox Background Sync

Sadly, testing BackgroundSync is somewhat unintuitive and difficult
for a number of reasons.

The best approach to test your implementation is to do the following:

1. Load up a page and register your service worker.
2. Turn off your computer's network or turn off your web server.
   - **DO NOT USE CHROME DEVTOOLS OFFLINE.** The offline checkbox in DevTools only affects requests from the page. Service Worker requests will continue to go through.
3. Make network requests that should be queued with Workbox Background Sync.
   - You can check the requests have been queued by looking in `Chrome DevTools > Application > IndexedDB > workbox-background-sync > requests`
4. Now turn on your network or web server.
5. Force an early `sync` event by going to
   `Chrome DevTools > Application > Service Workers`, entering the tag name of
   `workbox-background-sync:<your queue name>` where `<your queue name>` should be
   the name of the queue you set, and then clicking the 'Sync' button.

   ![Example of Sync button in Chrome DevTools](https://developer.chrome.com/static/docs/workbox/modules/workbox-background-sync/image/example-sync-button-chr-c623a306a9d2e.png)
6. You should see network requests go through for the failed requests and
   the IndexedDB data should now be empty since the requests have been
   successfully replayed.

<br />

## Types

### BackgroundSyncPlugin

A class implementing the `fetchDidFail` lifecycle callback. This makes it
easier to add failed requests to a background sync Queue.

#### Properties

- constructor void


  The `constructor` function looks like:

  ```typescript
  (name: string, options?: QueueOptions) => {...}
  ```

  <br />

  - name string

    See the [`workbox-background-sync.Queue`](https://developer.chrome.com/docs/workbox/modules/workbox-background-sync#type-Queue)
    documentation for parameter details.
  - options [QueueOptions](https://developer.chrome.com/docs/workbox/modules/workbox-background-sync#type-QueueOptions) optional
  - returns[BackgroundSyncPlugin](https://developer.chrome.com/docs/workbox/modules/workbox-background-sync#type-BackgroundSyncPlugin)

### Queue

A class to manage storing failed requests in IndexedDB and retrying them
later. All parts of the storing and replaying process are observable via
callbacks.

#### Properties

- constructor void

  Creates an instance of Queue with the given options


  The `constructor` function looks like:

  ```typescript
  (name: string, options?: QueueOptions) => {...}
  ```

  <br />

  - name string

    The unique name for this queue. This name must be
    unique as it's used to register sync events and store requests
    in IndexedDB specific to this instance. An error will be thrown if
    a duplicate name is detected.
  - options [QueueOptions](https://developer.chrome.com/docs/workbox/modules/workbox-background-sync#type-QueueOptions) optional
  - returns[Queue](https://developer.chrome.com/docs/workbox/modules/workbox-background-sync#type-Queue)
- name string
- getAll void

  Returns all the entries that have not expired (per `maxRetentionTime`).
  Any expired entries are removed from the queue.


  The `getAll` function looks like:

  ```typescript
  () => {...}
  ```

  <br />

  - returnsPromise\<QueueEntry\[\]\>
- popRequest void

  Removes and returns the last request in the queue (along with its
  timestamp and any metadata). The returned object takes the form:
  `{request, timestamp, metadata}`.


  The `popRequest` function looks like:

  ```typescript
  () => {...}
  ```

  <br />

  - returnsPromise\<QueueEntry\>
- pushRequest void

  Stores the passed request in IndexedDB (with its timestamp and any
  metadata) at the end of the queue.


  The `pushRequest` function looks like:

  ```typescript
  (entry: QueueEntry) => {...}
  ```

  <br />

  - entry QueueEntry
  - returnsPromise\<void\>
- registerSync void

  Registers a sync event with a tag unique to this instance.


  The `registerSync` function looks like:

  ```typescript
  () => {...}
  ```

  <br />

  - returnsPromise\<void\>
- replayRequests void

  Loops through each request in the queue and attempts to re-fetch it.
  If any request fails to re-fetch, it's put back in the same position in
  the queue (which registers a retry for the next sync event).


  The `replayRequests` function looks like:

  ```typescript
  () => {...}
  ```

  <br />

  - returnsPromise\<void\>
- shiftRequest void

  Removes and returns the first request in the queue (along with its
  timestamp and any metadata). The returned object takes the form:
  `{request, timestamp, metadata}`.


  The `shiftRequest` function looks like:

  ```typescript
  () => {...}
  ```

  <br />

  - returnsPromise\<QueueEntry\>
- size void

  Returns the number of entries present in the queue.
  Note that expired entries (per `maxRetentionTime`) are also included in this count.


  The `size` function looks like:

  ```typescript
  () => {...}
  ```

  <br />

  - returnsPromise\<number\>
- unshiftRequest void

  Stores the passed request in IndexedDB (with its timestamp and any
  metadata) at the beginning of the queue.


  The `unshiftRequest` function looks like:

  ```typescript
  (entry: QueueEntry) => {...}
  ```

  <br />

  - entry QueueEntry
  - returnsPromise\<void\>

### QueueOptions

#### Properties

- forceSyncFallback boolean optional
- maxRetentionTime number optional
- onSync OnSyncCallback optional

### QueueStore

A class to manage storing requests from a Queue in IndexedDB,
indexed by their queue name for easier access.

Most developers will not need to access this class directly;
it is exposed for advanced use cases.

#### Properties

- constructor void

  Associates this instance with a Queue instance, so entries added can be
  identified by their queue name.


  The `constructor` function looks like:

  ```typescript
  (queueName: string) => {...}
  ```

  <br />

  - queueName string
  - returns[QueueStore](https://developer.chrome.com/docs/workbox/modules/workbox-background-sync#type-QueueStore)
- deleteEntry void

  Deletes the entry for the given ID.

  WARNING: this method does not ensure the deleted entry belongs to this
  queue (i.e. matches the `queueName`). But this limitation is acceptable
  as this class is not publicly exposed. An additional check would make
  this method slower than it needs to be.


  The `deleteEntry` function looks like:

  ```typescript
  (id: number) => {...}
  ```

  <br />

  - id number
  - returnsPromise\<void\>
- getAll void

  Returns all entries in the store matching the `queueName`.


  The `getAll` function looks like:

  ```typescript
  () => {...}
  ```

  <br />

  - returnsPromise\<QueueStoreEntry\[\]\>
- popEntry void

  Removes and returns the last entry in the queue matching the `queueName`.


  The `popEntry` function looks like:

  ```typescript
  () => {...}
  ```

  <br />

  - returnsPromise\<QueueStoreEntry\>
- pushEntry void

  Append an entry last in the queue.


  The `pushEntry` function looks like:

  ```typescript
  (entry: UnidentifiedQueueStoreEntry) => {...}
  ```

  <br />

  - entry UnidentifiedQueueStoreEntry
  - returnsPromise\<void\>
- shiftEntry void

  Removes and returns the first entry in the queue matching the `queueName`.


  The `shiftEntry` function looks like:

  ```typescript
  () => {...}
  ```

  <br />

  - returnsPromise\<QueueStoreEntry\>
- size void

  Returns the number of entries in the store matching the `queueName`.


  The `size` function looks like:

  ```typescript
  () => {...}
  ```

  <br />

  - returnsPromise\<number\>
- unshiftEntry void

  Prepend an entry first in the queue.


  The `unshiftEntry` function looks like:

  ```typescript
  (entry: UnidentifiedQueueStoreEntry) => {...}
  ```

  <br />

  - entry UnidentifiedQueueStoreEntry
  - returnsPromise\<void\>

### StorableRequest

A class to make it easier to serialize and de-serialize requests so they
can be stored in IndexedDB.

Most developers will not need to access this class directly;
it is exposed for advanced use cases.

#### Properties

- constructor void

  Accepts an object of request data that can be used to construct a
  `Request` but can also be stored in IndexedDB.


  The `constructor` function looks like:

  ```typescript
  (requestData: RequestData) => {...}
  ```

  <br />

  - requestData RequestData

    An object of request data that includes the
    `url` plus any relevant properties of
    \[requestInit\]`https://fetch.spec.whatwg.org/#requestinit`.
  - returns[StorableRequest](https://developer.chrome.com/docs/workbox/modules/workbox-background-sync#type-StorableRequest)
- clone void

  Creates and returns a deep clone of the instance.


  The `clone` function looks like:

  ```typescript
  () => {...}
  ```

  <br />

  - returns[StorableRequest](https://developer.chrome.com/docs/workbox/modules/workbox-background-sync#type-StorableRequest)
- toObject void

  Returns a deep clone of the instances `_requestData` object.


  The `toObject` function looks like:

  ```typescript
  () => {...}
  ```

  <br />

  - returnsRequestData
- toRequest void

  Converts this instance to a Request.


  The `toRequest` function looks like:

  ```typescript
  () => {...}
  ```

  <br />

  - returnsRequest
- fromRequest void

  Converts a Request object to a plain object that can be structured
  cloned or JSON-stringified.


  The `fromRequest` function looks like:

  ```typescript
  (request: Request) => {...}
  ```

  <br />

  - request Request
  - returnsPromise\<[StorableRequest](https://developer.chrome.com/docs/workbox/modules/workbox-background-sync#type-StorableRequest)\>

<br />