# The Cache API: A quick guide

The [Cache API][mdn-cache] is a system for storing and retrieving network
requests and their corresponding responses. These might be regular requests
and responses created in the course of running your application, or they could
be created solely for the purpose of storing data for later use.

The Cache API was created to enable service workers to cache network requests
so that they can provide fast responses, regardless of network speed or
availability. However, the API can also be used as a general storage mechanism.

## Where is it available?

The Cache API is available in [all modern browsers][caniuse-cache]. It is
exposed via the global `caches` property, so you can test for the presence of
the API with a simple feature detection:

```js
const cacheAvailable = 'caches' in self;
```

{% BrowserCompat 'api.Cache' %}

The Cache API can be accessed from a window, iframe, worker, or service worker.

## What can be stored

The caches only store pairs of [`Request`][mdn-request] and
[`Response`][mdn-response] objects, representing HTTP requests and responses,
respectively. However, the requests and responses can contain any kind of data
that can be transferred over HTTP.

### How much can be stored?

In short, **a lot**, at least a couple of hundred megabytes, and potentially
hundreds of gigabytes or more. Browser implementations vary, but the amount
of storage available is usually based on the amount of storage available on
the device.

## Creating and opening a cache

To open a cache, use the `caches.open(name)` method, passing the name of the
cache as the single parameter. If the named cache does not exist, it is
created. This method returns a `Promise` that resolves with the `Cache` object.

```js
const cache = await caches.open('my-cache');
// do something with cache...
```

## Adding to a cache

There are three ways to add an item to a cache - `add`, `addAll`, and `put`.
All three methods return a `Promise`.

### `cache.add`

First, there is `cache.add()`. It takes one parameter, either a `Request`
or a URL (`string`). It makes a request to the network and stores the response
in the cache. If the
fetch fails, or if the status code of the response is not in the 200 range,
then nothing is stored and the `Promise` rejects. Note that cross-origin
requests not in CORS mode cannot be stored because they return a `status` of
`0`. Such requests can only be stored with `put`.

```js
// Retreive data.json from the server and store the response.
cache.add(new Request('/data.json'));

// Retreive data.json from the server and store the response.
cache.add('/data.json');
```

### `cache.addAll`

Next, there is `cache.addAll()`. It works similarly to `add()`, but takes an
array of `Request` objects or URLs (`string`s). This works similarly to
calling `cache.add` for each individual request, except that the `Promise`
rejects if any single request is not cached.

```js
const urls = ['/weather/today.json', '/weather/tomorrow.json'];
cache.addAll(urls);
```

In each of these cases, a new entry overwrites any matching existing entry.
This uses the same matching rules described in the section on
[retrieving](#retrieving).

### `cache.put`

Finally, there is `cache.put()`, which allows you to store either a response
from the network, or create and store your own `Response`. It takes two
parameters. The first can either be a `Request` object or a URL (`string`).
The second must be a `Response`, either from the network, or generated by your
code.

```js
// Retrieve data.json from the server and store the response.
cache.put('/data.json');

// Create a new entry for test.json and store the newly created response.
cache.put('/test.json', new Response('{"foo": "bar"}'));

// Retrieve data.json from the 3rd party site and store the response.
cache.put('https://example.com/data.json');
```

The `put()` method is more permissive than either `add()` or `addAll()`, and
will allow you to store non-CORS responses, or other responses where the status
code of the response is not in the 200 range. It will overwrite any previous
responses for the same request.

#### Creating Request objects

Create the `Request` object using a URL for the thing being stored:

```js
const request = new Request('/my-data-store/item-id');
```

#### Working with Response objects

The `Response` object constructor accepts many types of data, including
`Blob`s, `ArrayBuffer`s, `FormData` objects, and strings.

```js
const imageBlob = new Blob([data], {type: 'image/jpeg'});
const imageResponse = new Response(imageBlob);
const stringResponse = new Response('Hello world');
```

You can set the MIME type of a `Response` by setting the appropriate header.

```js
  const options = {
    headers: {
      'Content-Type': 'application/json'
    }
  }
  const jsonResponse = new Response('{}', options);
```

If you have retrieved a `Response` and wish to access its body, there are
several helper methods you can use. Each returns a `Promise` that resolves
with a value of a different type.

<table>
  <thead>
    <th>Method</th>
    <th>Description</th>
  </thead>
  <tbody>
    <tr>
      <td><code>arrayBuffer</code></td>
      <td>
        Returns an <code>ArrayBuffer</code> containing the body, serialized to
        bytes.
      </td>
    </tr>
    <tr>
      <td><code>blob</code></td>
      <td>
        Returns a <code>Blob</code>. If the <code>Response</code> was created
        with a <code>Blob</code> then this new <code>Blob</code> has the same
        type. Otherwise, the <code>Content-Type</code> of the
        <code>Response</code> is used.
      </td>
    </tr>
    <tr>
      <td><code>text</code></td>
      <td>
        Interprets the bytes of the body as a UTF-8 encoded string.
      </td>
    </tr>
    <tr>
      <td><code>json</code></td>
      <td>
        Interprets the bytes of the body as a UTF-8 encoded string, then tries
        to parse it as JSON. Returns the resulting object, or throws a
        <code>TypeError</code> if the string cannot be parsed as JSON.
      </td>
    </tr>
    <tr>
      <td><code>formData</code></td>
      <td>
        Interprets the bytes of the body as an HTML form, encoded either as
        <code>multipart/form-data</code> or
        <code>application/x-www-form-urlencoded</code>. Returns a
        <a href="https://developer.mozilla.org/docs/Web/API/FormData">FormData</a>
        object, or throws a <code>TypeError</code> if the data cannot be parsed.
      </td>
    </tr>
    <tr>
      <td><code>body</code></td>
      <td>
        Returns a <a href="https://developer.mozilla.org/docs/Web/API/ReadableStream">ReadableStream</a>
        for the body data.
      </td>
    </tr>
  </tbody>
</table>

For example

```js
const response = new Response('Hello world');
const buffer = await response.arrayBuffer();
console.log(new Uint8Array(buffer));
// Uint8Array(11) [72, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100]
```

## Retrieving from a cache {: #retrieving }

To find an item in a cache, you can use the `match` method.

```js
const response = await cache.match(request);
console.log(request, response);
```

If `request` is a string the browser converts it to a `Request` by calling
`new Request(request)`. The function returns a `Promise` that resolves to
a `Response` if a matching entry is found, or `undefined` otherwise.

To determine if two `Requests` match, the browser uses more than just the URL. Two
requests are considered different if they have different query strings,
`Vary` headers, or HTTP methods (`GET`, `POST`, `PUT`, etc.).

You can ignore some or all of these things by passing an options object as a
second parameter.

```js
const options = {
  ignoreSearch: true,
  ignoreMethod: true,
  ignoreVary: true
};

const response = await cache.match(request, options);
// do something with the response
```

If more than one cached request matches then the one that was created first is
returned. If you want to retrieve *all* matching responses, you can use
`cache.matchAll()`.

```js
const options = {
  ignoreSearch: true,
  ignoreMethod: true,
  ignoreVary: true
};

const responses = await cache.matchAll(request, options);
console.log(`There are ${responses.length} matching responses.`);
```

As a shortcut you can search over all caches at once by using `caches.match()`
instead of calling `cache.match()` for each cache.

## Searching

The Cache API does not provide a way to search for requests or responses
except for matching entries against a `Response` object. However, you can
implement your own search using filtering or by creating an index.

### Filtering

One way to implement your own search is to iterate over all entries and
filter down to the ones that you want. Let's say that you want to find all
items that have URLs ending with `.png`.

```js
async function findImages() {
  // Get a list of all of the caches for this origin
  const cacheNames = await caches.keys();
  const result = [];

  for (const name of cacheNames) {
    // Open the cache
    const cache = await caches.open(name);

    // Get a list of entries. Each item is a Request object
    for (const request of await cache.keys()) {
      // If the request URL matches, add the response to the result
      if (request.url.endsWith('.png')) {
        result.push(await cache.match(request));
      }
    }
  }

  return result;
}
```

This way you can use any property of the `Request` and `Response` objects to
filter the entries. Note that this is slow if you search over large sets of
data.

### Creating an index

The other way to implement your own search is to maintain a separate index of
entries that can be searched and store the index in IndexedDB. Since this is the kind of
operation that IndexedDB was designed for it has much better performance with
large numbers of entries.

If you store the URL of the `Request` alongside the searchable properties
then you can easily retrieve the correct cache entry after doing the search.

## Deleting an item

To delete an item from a cache:

```js
cache.delete(request);
```

Where request can be a `Request` or a URL string. This method also takes the
same options object as `cache.match`, which allows you to delete multiple
`Request`/`Response` pairs for the same URL.

```js
cache.delete('/example/file.txt', {ignoreVary: true, ignoreSearch: true});
```

## Deleting a cache

To delete a cache, call `caches.delete(name)`. This function returns a
`Promise` that resolves to `true` if the cache existed and was deleted, or
`false` otherwise.

## Thanks

Thanks to Mat Scales who wrote the original version of this article, which
first appeared on WebFundamentals.

[mdn-cache]: https://developer.mozilla.org/docs/Web/API/Cache
[caniuse-cache]: https://caniuse.com/#feat=mdn-api_cache
[mdn-request]: https://developer.mozilla.org/docs/Web/API/Request
[mdn-response]: https://developer.mozilla.org/docs/Web/API/Response