# Cache API

A Cache API do Azion Runtime permite que uma function leia e grave no [Cache](/pt-br/documentacao/plataforma/applications/cache/expiracao-e-atualizacao/). O objeto global `caches` é um `CacheStorage` que abre caches pelo nome. Cada `Cache` que ele retorna armazena objetos `Response` sob uma chave, de modo que uma function pode guardar uma resposta que construiu e retorná-la novamente.

> **nota**
>
> Com `azion dev`, `caches` não está definido, e qualquer chamada lança `ReferenceError: caches is not defined`. O formato de handler `export async function fetch` também interrompe o `azion dev` com `SyntaxError: Unexpected token 'export'`. Teste o código da Cache API em uma function com deploy feito.

---

## Acesso

Uma function acessa a Cache API por meio do objeto global `caches`. Abra um cache pelo nome com `caches.open()` e, em seguida, chame os métodos de `Cache` no objeto que ele retorna:

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

---

## CacheStorage

O objeto global `caches` é um `CacheStorage`. Ele tem três métodos, e cada um recebe o nome de um cache.

| Método         | Retorna  | Descrição                                                                                                    |
| -------------- | -------- | ------------------------------------------------------------------------------------------------------------ |
| `open(name)`   | `Cache`  | Abre o cache com esse nome.                                                                                  |
| `has(name)`    | booleano | `true` quando o cache foi aberto antes na mesma requisição. `false` para um nome que a requisição não abriu. |
| `delete(name)` | booleano | Exclui o cache com esse nome. Retorna `false` para um nome que a requisição não abriu.                       |

`caches.keys()` e `caches.default` não estão disponíveis: os dois são `undefined`.

---

## Cache

`caches.open()` retorna um `Cache`. Um `Cache` armazena cada resposta sob uma chave, que é um objeto `Request` ou uma string de URL.

| Método                   | Retorna                    | Descrição                                                                                                                               |
| ------------------------ | -------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- |
| `match(request)`         | `Response` ou nenhum valor | Retorna a resposta armazenada sob a chave. Quando a chave não tem entrada, a promise é resolvida sem resposta, e `if (cached)` é falso. |
| `put(request, response)` | —                          | Armazena a resposta sob a chave. O método da requisição precisa ser `GET`.                                                              |
| `delete(request)`        | booleano                   | Remove a entrada da chave e retorna `true`. Um `match()` posterior para a chave não retorna resposta.                                   |

`matchAll()`, `add()`, `addAll()` e `keys()` não estão disponíveis em um `Cache`.

---

## Respostas armazenadas

O header `cache-control` de uma resposta não impede que `put()` a armazene. Uma resposta com `cache-control: no-store` é armazenada, e `match()` a retorna na mesma requisição.

Uma resposta armazenada com `cache-control: max-age=600` é encontrada por requisições posteriores. Depois que `delete()` remove a entrada, `match()` não retorna resposta.

---

## Exemplo

A function abaixo executa operações de `CacheStorage` e de `Cache` escolhidas pelos headers da requisição. Ela lê três headers:

| Header                | Valores                                 | Descrição                                                                                                                                         |
| --------------------- | --------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- |
| `x-storage-operation` | `open`, `has`, `delete`                 | A operação de `CacheStorage` a executar.                                                                                                          |
| `x-cache-operation`   | `match`, `put`, `delete`, `none`, `all` | A operação de `Cache` a executar depois de `open`. `none` apenas abre o cache, e `all` executa `put`, `match` e `delete` em uma única requisição. |
| `x-storage-name`      | Qualquer string                         | Nome do cache. Opcional; o padrão é `my-cache`.                                                                                                   |

Em uma function com deploy feito, o argumento `env` é um objeto vazio, então uma requisição sem `x-storage-name` usa `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);
  }
}
```

A function responde às requisições enviadas na ordem abaixo com estas respostas:

| `x-storage-operation` | `x-cache-operation` | Status | Corpo                                      |
| --------------------- | ------------------- | ------ | ------------------------------------------ |
| Não enviado           | Não enviado         | 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`                 | Não enviado         | 200    | `Cache storage 'my-cache' does NOT exist.` |
| `delete`              | Não enviado         | 200    | `Cache storage 'my-cache' doesn't exist.`  |
| `bogus`               | Não enviado         | 400    | `Invalid cache storage operation`          |

A resposta que essa function armazena não tem header `cache-control`, e uma requisição `match` após a requisição `put` responde 404. Uma resposta armazenada com `cache-control: max-age` é encontrada por requisições posteriores, como descreve a seção Respostas armazenadas. As requisições `has` e `delete` não abrem o cache, e as duas informam que `my-cache` não existe.

---

## Erros

| Erro                                    | Causa                                                                | Solução                                                            |
| --------------------------------------- | -------------------------------------------------------------------- | ------------------------------------------------------------------ |
| `TypeError: Request method must be GET` | `put()` recebeu uma requisição cujo método não é `GET`, como `POST`. | Armazene a resposta sob uma requisição `GET` ou uma string de URL. |
| `ReferenceError: caches is not defined` | O código roda com `azion dev`, que não fornece `caches`.             | Teste o código em uma function com deploy feito.                   |

---

## Recursos relacionados

- [Handlers](/pt-br/documentacao/devtools/runtime/api-reference/handlers.md): Os formatos de handler que uma function exporta e os argumentos que cada um recebe.
- [Response](/pt-br/documentacao/devtools/runtime/api-reference/response.md): O objeto `Response` que `put()` armazena e `match()` retorna.
- [Request](/pt-br/documentacao/devtools/runtime/api-reference/request.md): O objeto `Request` que um `Cache` usa como chave de uma entrada.
- [Cache settings](/pt-br/documentacao/plataforma/applications/cache/cache-settings.md): As configurações que uma aplicação usa para fazer cache de respostas sem uma function.
