Перейти к содержимому

Кэширование и условные запросы

Каждый ответ GET от Bordio API содержит ETag. Отправив его обратно в следующем запросе, вы можете спросить «изменилось ли это?» и получить крошечный 304 Not Modified вместо полного тела, если ничего не изменилось.

Большинство данных, которые вы читаете — списки задач, определения, кастомные поля — меняются гораздо реже, чем вы их опрашиваете. Без кэширования каждый GET заново скачивает идентичное тело. Условные запросы позволяют:

  • Экономить трафик — неизменённый ресурс возвращается как пустой 304 вместо полной нагрузки.
  • Дёшево ревалидировать — один round-trip подтверждает, что ваша закэшированная копия всё ещё актуальна.
  • Упростить логику кэша — сервер сам сообщает, когда обновиться; вам не нужно гадать.

На каждый GET API устанавливает два заголовка:

ETag: "e3b0c44298fc1c149afbf4c8996fb924"
Cache-Control: private, no-cache
  • ETag — это сильный валидатор, вычисленный по точному телу ответа: если тело хоть как-то меняется, ETag меняется.
  • Cache-Control: private, no-cache означает: вы можете сохранить ответ, но обязаны ревалидировать его на сервере перед повторным использованием (не отдавайте его вслепую), и он приватен для этого ключа — никогда не кэшируйте его в общем/CDN-кэше.

Это применяется ко всем эндпоинтам GET — задачи, подзадачи, события, проекты, участники и любой ресурс определений (статусы задач, типы, кастомные поля, теги…).

  1. При GET сохраните тело ответа вместе с его ETag.
  2. При следующем GET того же ресурса отправьте сохранённое значение в заголовке If-None-Match.
  3. Сервер сравнивает его с текущим ETag:
    • Совпадение304 Not Modified с пустым телом. Используйте свою закэшированную копию.
    • Нет совпадения200 OK с новым телом и новым ETag. Замените закэшированную копию и сохранённый ETag.
Terminal window
# Первый запрос — обратите внимание на 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"
# Позже — ревалидация с сохранённым ETag
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 (нет тела — ваша закэшированная копия всё ещё актуальна)

Минимальный клиентский кэш:

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; // не изменилось — используем кэш
const body = await res.json();
cache.set(url, { etag: res.headers.get('ETag'), body });
return body;
}

If-None-Match сравнивается с помощью функции слабого сравнения (RFC 7232 §3.2), поэтому ревалидация нетребовательна к префиксу слабого валидатора:

  • Префикс W/ с любой стороны игнорируется — W/"abc" и "abc" считаются одним и тем же тегом.
  • Вы можете отправить список тегов через запятую; совпадение с любым из них удовлетворяет условию: If-None-Match: "abc", W/"def".
  • Вы можете отправить *, который совпадает с любым текущим представлением.

Обычно вы просто возвращаете обратно точную строку ETag, которую получили — слабое сравнение лишь означает, что тег с префиксом W/ от вашего HTTP-клиента всё равно совпадёт.

  • Считайте ETag непрозрачным. Это хеш — не разбирайте его, сравнивайте только на равенство, храните дословно.
  • 304 всё равно считается запросом в счёт вашего лимита запросов и учёта. Условные запросы экономят трафик, а не ваш бюджет запросов — поэтому держите разумный интервал опроса.
  • ETag привязан к представлению. Разные query-параметры (например, другой project_id, курсор страницы или фильтр) дают разные тела и, следовательно, разные ETag. Кэшируйте их по полному URL.
  • Условные запросы применяются только к чтениям (GET). Записи (POST/PATCH/DELETE) здесь никогда не бывают условными — для безопасных повторов записей используйте ключи идемпотентности.