Кэширование и условные запросы
Каждый ответ GET от Bordio API содержит ETag. Отправив его обратно в следующем запросе, вы можете спросить «изменилось ли это?» и получить крошечный 304 Not Modified вместо полного тела, если ничего не изменилось.
Зачем это нужно
Заголовок раздела «Зачем это нужно»Большинство данных, которые вы читаете — списки задач, определения, кастомные поля — меняются гораздо реже, чем вы их опрашиваете. Без кэширования каждый GET заново скачивает идентичное тело. Условные запросы позволяют:
- Экономить трафик — неизменённый ресурс возвращается как пустой
304вместо полной нагрузки. - Дёшево ревалидировать — один round-trip подтверждает, что ваша закэшированная копия всё ещё актуальна.
- Упростить логику кэша — сервер сам сообщает, когда обновиться; вам не нужно гадать.
Как ведёт себя сервер
Заголовок раздела «Как ведёт себя сервер»На каждый GET API устанавливает два заголовка:
ETag: "e3b0c44298fc1c149afbf4c8996fb924"Cache-Control: private, no-cacheETag— это сильный валидатор, вычисленный по точному телу ответа: если тело хоть как-то меняется,ETagменяется.Cache-Control: private, no-cacheозначает: вы можете сохранить ответ, но обязаны ревалидировать его на сервере перед повторным использованием (не отдавайте его вслепую), и он приватен для этого ключа — никогда не кэшируйте его в общем/CDN-кэше.
Это применяется ко всем эндпоинтам GET — задачи, подзадачи, события, проекты, участники и любой ресурс определений (статусы задач, типы, кастомные поля, теги…).
Как это использовать
Заголовок раздела «Как это использовать»- При
GETсохраните тело ответа вместе с егоETag. - При следующем
GETтого же ресурса отправьте сохранённое значение в заголовкеIf-None-Match. - Сервер сравнивает его с текущим
ETag:- Совпадение →
304 Not Modifiedс пустым телом. Используйте свою закэшированную копию. - Нет совпадения →
200 OKс новым телом и новымETag. Замените закэшированную копию и сохранённыйETag.
- Совпадение →
# Первый запрос — обратите внимание на ETagcurl -i "https://api.bordio.com/public/v1/custom_fields?project_id=proj_6594a1b2c3d4e5f6a7b8c9d0" \ -H "Authorization: Bearer brd_sk_live_..."# → 200 OK# ETag: "e3b0c44298fc1c149afbf4c8996fb924"
# Позже — ревалидация с сохранённым ETagcurl -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) здесь никогда не бывают условными — для безопасных повторов записей используйте ключи идемпотентности.