Pular para o conteúdo

Cache e requisições condicionais

Toda resposta GET da API do Bordio carrega um ETag. Ao enviá-lo de volta na sua próxima requisição, você pode perguntar “isso mudou?” e receber um pequeno 304 Not Modified em vez do corpo completo quando nada mudou.

A maior parte dos dados que você lê — listas de tarefas, definições, campos personalizados — muda com muito menos frequência do que você faz sondagem. Sem cache, todo GET baixa novamente um corpo idêntico. As requisições condicionais permitem que você:

  • Economize largura de banda — um recurso inalterado retorna como um 304 vazio em vez do payload completo.
  • Revalide de forma econômica — uma única ida e volta confirma que sua cópia em cache ainda está atual.
  • Simplifique a lógica de cache — o servidor informa quando atualizar; você nunca precisa adivinhar.

Em todo GET, a API define dois cabeçalhos:

ETag: "e3b0c44298fc1c149afbf4c8996fb924"
Cache-Control: private, no-cache
  • O ETag é um validador forte calculado sobre o corpo exato da resposta — se o corpo mudar de qualquer forma, o ETag muda.
  • Cache-Control: private, no-cache significa: você pode armazenar a resposta, mas deve revalidá-la com o servidor antes de reutilizá-la (não a sirva às cegas), e ela é privada desta chave — nunca a armazene em um cache compartilhado/CDN.

Isso se aplica a todos os endpoints GET — tarefas, subtarefas, eventos, projetos, membros e todos os recursos de definições (status de tarefas, tipos, campos personalizados, tags…).

  1. Em um GET, armazene o corpo da resposta junto com seu ETag.
  2. No seu próximo GET do mesmo recurso, envie o valor armazenado em um cabeçalho If-None-Match.
  3. O servidor o compara com o ETag atual:
    • Correspondência304 Not Modified com um corpo vazio. Reutilize sua cópia em cache.
    • Sem correspondência200 OK com o novo corpo e um novo ETag. Substitua sua cópia em cache e o ETag armazenado.
Terminal window
# Primeira requisição — observe o ETag
curl -i "https://api.bordio.com/public/v1/custom_fields?project_id=proj_6594a1b2c3d4e5f6a7b8c9d0" \
-H "Authorization: Bearer brd_sk_live_..."
# → 200 OK
# ETag: "e3b0c44298fc1c149afbf4c8996fb924"
# Mais tarde — revalide com o ETag armazenado
curl -i "https://api.bordio.com/public/v1/custom_fields?project_id=proj_6594a1b2c3d4e5f6a7b8c9d0" \
-H "Authorization: Bearer brd_sk_live_..." \
-H 'If-None-Match: "e3b0c44298fc1c149afbf4c8996fb924"'
# → 304 Not Modified (sem corpo — sua cópia em cache ainda está atual)

Um cache de cliente mínimo:

const cache = new Map(); // url -> { etag, body }
async function getCached(url, apiKey) {
const prev = cache.get(url);
const res = await fetch(url, {
headers: {
Authorization: `Bearer ${apiKey}`,
...(prev ? { 'If-None-Match': prev.etag } : {}),
},
});
if (res.status === 304) return prev.body; // inalterado — reutiliza o cache
const body = await res.json();
cache.set(url, { etag: res.headers.get('ETag'), body });
return body;
}

If-None-Match é comparado usando a função de comparação fraca (RFC 7232 §3.2), então a revalidação é tolerante quanto ao prefixo de validador fraco:

  • Um prefixo W/ em qualquer lado é ignorado — W/"abc" e "abc" são tratados como a mesma tag.
  • Você pode enviar uma lista separada por vírgulas de tags; uma correspondência em qualquer uma satisfaz a condição: If-None-Match: "abc", W/"def".
  • Você pode enviar *, que corresponde a qualquer representação atual.

Normalmente você apenas devolve a string exata do ETag que recebeu — a comparação fraca simplesmente significa que uma tag com prefixo W/ do seu cliente HTTP ainda corresponde.

  • Trate o ETag como opaco. É um hash — não o analise, compare apenas por igualdade, armazene-o literalmente.
  • Um 304 ainda conta como uma requisição no seu limite de taxa e uso. As requisições condicionais economizam largura de banda, não o seu orçamento de requisições — então mantenha um intervalo de sondagem sensato.
  • O ETag é por representação. Parâmetros de consulta diferentes (por exemplo, um project_id diferente, cursor de página ou filtro) produzem corpos diferentes e, portanto, ETags diferentes. Faça cache deles por URL completa.
  • As requisições condicionais se aplicam apenas a leituras (GET). As escritas (POST/PATCH/DELETE) nunca são condicionais aqui — para novas tentativas seguras de escritas, use chaves de idempotência em vez disso.