# Cache API

The Azion Runtime Cache API lets a function read from and write to [Cache](/en/documentation/platform/applications/cache/expiration-and-freshness/). The global `caches` object is a `CacheStorage` that opens caches by name. Each `Cache` it returns stores `Response` objects under a key, so a function can keep a response it built and return it again.

> **Note**
>
> Under `azion dev`, `caches` is not defined, and any call throws `ReferenceError: caches is not defined`. The `export async function fetch` handler shape also stops `azion dev` with `SyntaxError: Unexpected token 'export'`. Test Cache API code on a deployed function.

---

## Access

A function reaches the Cache API through the global `caches` object. Open a cache by name with `caches.open()`, then call the `Cache` methods on the object it returns:

```javascript
const cache = await caches.open("my-cache");
```

---

## CacheStorage

The global `caches` object is a `CacheStorage`. It has three methods, and each one takes the name of a cache.

| Method         | Returns | Description                                                                                                  |
| -------------- | ------- | ------------------------------------------------------------------------------------------------------------ |
| `open(name)`   | `Cache` | Opens the cache with this name.                                                                              |
| `has(name)`    | boolean | `true` when the cache was opened earlier in the same request. `false` for a name the request has not opened. |
| `delete(name)` | boolean | Deletes the cache with this name. Returns `false` for a name the request has not opened.                     |

`caches.keys()` and `caches.default` are not available: both are `undefined`.

---

## Cache

`caches.open()` returns a `Cache`. A `Cache` stores each response under a key, which is a `Request` object or a URL string.

| Method                   | Returns                 | Description                                                                                                                            |
| ------------------------ | ----------------------- | -------------------------------------------------------------------------------------------------------------------------------------- |
| `match(request)`         | `Response`, or no value | Returns the response stored under the key. When the key has no entry, the promise resolves to no response, and `if (cached)` is false. |
| `put(request, response)` | —                       | Stores the response under the key. The request method must be `GET`.                                                                   |
| `delete(request)`        | boolean                 | Removes the entry for the key and returns `true`. A later `match()` for the key returns no response.                                   |

`matchAll()`, `add()`, `addAll()`, and `keys()` are not available on a `Cache`.

---

## Stored responses

The `cache-control` header of a response does not stop `put()` from storing it. A response with `cache-control: no-store` is stored, and `match()` returns it in the same request.

A response stored with `cache-control: max-age=600` is matched by later requests. After `delete()` removes the entry, `match()` returns no response.

---

## Example

The function below runs `CacheStorage` and `Cache` operations chosen by request headers. It reads three headers:

| Header                | Values                                  | Description                                                                                                                         |
| --------------------- | --------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------- |
| `x-storage-operation` | `open`, `has`, `delete`                 | The `CacheStorage` operation to run.                                                                                                |
| `x-cache-operation`   | `match`, `put`, `delete`, `none`, `all` | The `Cache` operation to run after `open`. `none` only opens the cache, and `all` runs `put`, `match`, and `delete` in one request. |
| `x-storage-name`      | Any string                              | Name of the cache. Optional; the default is `my-cache`.                                                                             |

Deployed, the `env` argument is an empty object, so a request without `x-storage-name` uses `my-cache`.

```javascript
function generateResponse(message, status) {
  return new Response(message, {
    headers: { "content-type": "text/plain" },
    status,
  });
}

async function runCacheOperation(cache, op, request) {
  switch (op) {
    case "none":
      return generateResponse("success in cache open!", 200);

    case "match": {
      console.log("[INFO] 'match' operation");
      const cached = await cache.match(request);
      if (cached) {
        console.log("[INFO] Cache hit for: " + request.url);
        return cached;
      }
      console.log("[INFO] No content cached");
      return generateResponse("No content cached", 404);
    }

    case "put": {
      console.log("[INFO] 'put' operation");
      const resp = generateResponse("my content", 200);
      await cache.put(request, resp.clone());
      return resp;
    }

    case "delete":
      console.log("[INFO] 'delete' operation");
      await cache.delete(request);
      return generateResponse("OK", 200);

    case "all": {
      console.log("[INFO] 'all' operations (put -> match -> delete)");
      const resp = generateResponse("my content", 200);
      await cache.put(request, resp.clone());
      await cache.match(request);
      await cache.delete(request);
      return generateResponse("all ops ok", 200);
    }

    default:
      console.log("[INFO] invalid cache operation");
      return generateResponse("Invalid cache operation", 400);
  }
}

export async function fetch(request, env, ctx) {
  const storageOp = request.headers.get("x-storage-operation");
  const cacheOp   = request.headers.get("x-cache-operation");
  const storageName =
    request.headers.get("x-storage-name") ||
    env.DEFAULT_CACHE_NAME ||           // optional
    "my-cache";

  try {
    switch (storageOp) {
      /* ---------- cacheStorage.open ---------- */
      case "open": {
        console.log("[INFO] cache storage 'open' operation");
        const cache = await caches.open(storageName);
        console.log("[INFO] storage cache '" + storageName + "' opened");
        return runCacheOperation(cache, cacheOp, request);
      }

      /* ---------- cacheStorage.has ---------- */
      case "has": {
        console.log("[INFO] cache storage 'has' operation");
        const exists = await caches.has(storageName);
        const msg = exists
          ? "Cache storage '" + storageName + "' exists."
          : "Cache storage '" + storageName + "' does NOT exist.";
        console.log("[INFO] " + msg);
        return generateResponse(msg, 200);
      }

      /* ---------- cacheStorage.delete ---------- */
      case "delete": {
        console.log("[INFO] cache storage 'delete' operation");
        const deleted = await caches.delete(storageName);
        const msg = deleted
          ? "Success deleting cache storage '" + storageName + "'."
          : "Cache storage '" + storageName + "' doesn't exist.";
        console.log("[INFO] " + msg);
        return generateResponse(msg, 200);
      }

      /* ---------- invalid op ---------- */
      default:
        console.log("[INFO] invalid cache storage operation");
        return generateResponse("Invalid cache storage operation", 400);
    }
  } catch (err) {
    console.error("[ERROR]", err);
    return generateResponse(err.message, 500);
  }
}
```

The function answers requests sent in the order below with these responses:

| `x-storage-operation` | `x-cache-operation` | Status | Body                                       |
| --------------------- | ------------------- | ------ | ------------------------------------------ |
| Not sent              | Not sent            | 400    | `Invalid cache storage operation`          |
| `open`                | `none`              | 200    | `success in cache open!`                   |
| `open`                | `put`               | 200    | `my content`                               |
| `open`                | `match`             | 404    | `No content cached`                        |
| `open`                | `delete`            | 200    | `OK`                                       |
| `open`                | `all`               | 200    | `all ops ok`                               |
| `has`                 | Not sent            | 200    | `Cache storage 'my-cache' does NOT exist.` |
| `delete`              | Not sent            | 200    | `Cache storage 'my-cache' doesn't exist.`  |
| `bogus`               | Not sent            | 400    | `Invalid cache storage operation`          |

The response this function stores carries no `cache-control` header, and a `match` request after the `put` request answers 404. A response stored with `cache-control: max-age` is matched by later requests, as Stored responses describes. The `has` and `delete` requests do not open the cache, and both report that `my-cache` does not exist.

---

## Errors

| Error                                   | Cause                                                                 | Fix                                                       |
| --------------------------------------- | --------------------------------------------------------------------- | --------------------------------------------------------- |
| `TypeError: Request method must be GET` | `put()` received a request whose method is not `GET`, such as `POST`. | Store the response under a `GET` request or a URL string. |
| `ReferenceError: caches is not defined` | The code runs under `azion dev`, which does not provide `caches`.     | Test the code on a deployed function.                     |

---

## Related resources

- [Handlers](/en/documentation/devtools/runtime/api-reference/handlers.md): The handler shapes a function exports, and the arguments each one receives.
- [Response](/en/documentation/devtools/runtime/api-reference/response.md): The `Response` object that `put()` stores and `match()` returns.
- [Request](/en/documentation/devtools/runtime/api-reference/request.md): The `Request` object a `Cache` uses as the key of an entry.
- [Cache settings](/en/documentation/platform/applications/cache/cache-settings.md): The settings an application uses to cache responses without a function.
