Almacenamiento en caché y solicitudes condicionales
Cada respuesta GET de la API de Bordio incluye un ETag. Al reenviarlo en tu siguiente solicitud puedes preguntar “¿ha cambiado esto?” y obtener un pequeño 304 Not Modified en lugar del cuerpo completo cuando nada ha cambiado.
Por qué existe
Sección titulada «Por qué existe»La mayoría de los datos que lees — listas de tareas, definiciones, campos personalizados — cambian mucho menos de lo que los sondeas. Sin almacenamiento en caché, cada GET vuelve a descargar un cuerpo idéntico. Las solicitudes condicionales te permiten:
- Ahorrar ancho de banda — un recurso sin cambios regresa como un
304vacío en lugar de la carga útil completa. - Revalidar de forma económica — un solo viaje de ida y vuelta confirma que tu copia en caché sigue vigente.
- Simplificar la lógica de caché — el servidor te dice cuándo actualizar; nunca lo adivinas.
Cómo se comporta el servidor
Sección titulada «Cómo se comporta el servidor»En cada GET, la API establece dos cabeceras:
ETag: "e3b0c44298fc1c149afbf4c8996fb924"Cache-Control: private, no-cache- El
ETages un validador fuerte calculado sobre el cuerpo exacto de la respuesta — si el cuerpo cambia de cualquier manera, elETagcambia. Cache-Control: private, no-cachesignifica: puedes almacenar la respuesta, pero debes revalidarla con el servidor antes de reutilizarla (no la sirvas a ciegas), y es privada para esta clave — nunca la almacenes en una caché compartida/CDN.
Esto se aplica a todos los endpoints GET — tareas, subtareas, eventos, proyectos, miembros y todos los recursos de definiciones (estados de tarea, tipos, campos personalizados, etiquetas…).
Cómo usarlo
Sección titulada «Cómo usarlo»- En un
GET, almacena el cuerpo de la respuesta junto con suETag. - En tu siguiente
GETdel mismo recurso, envía el valor almacenado en una cabeceraIf-None-Match. - El servidor lo compara con el
ETagactual:- Coincidencia →
304 Not Modifiedcon un cuerpo vacío. Reutiliza tu copia en caché. - Sin coincidencia →
200 OKcon el nuevo cuerpo y un nuevoETag. Reemplaza tu copia en caché y elETagalmacenado.
- Coincidencia →
# Primera solicitud — fíjate en el ETagcurl -i "https://api.bordio.com/public/v1/custom_fields?project_id=proj_6594a1b2c3d4e5f6a7b8c9d0" \ -H "Authorization: Bearer brd_sk_live_..."# → 200 OK# ETag: "e3b0c44298fc1c149afbf4c8996fb924"
# Más tarde — revalida con el ETag almacenadocurl -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 (sin cuerpo — tu copia en caché sigue vigente)Una caché de cliente mínima:
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; // sin cambios — reutiliza la caché
const body = await res.json(); cache.set(url, { etag: res.headers.get('ETag'), body }); return body;}Comparación débil
Sección titulada «Comparación débil»If-None-Match se compara usando la función de comparación débil (RFC 7232 §3.2), por lo que la revalidación es tolerante respecto al prefijo del validador débil:
- Un prefijo
W/en cualquiera de los dos lados se ignora —W/"abc"y"abc"se tratan como la misma etiqueta. - Puedes enviar una lista separada por comas de etiquetas; una coincidencia con cualquiera de ellas satisface la condición:
If-None-Match: "abc", W/"def". - Puedes enviar
*, que coincide con cualquier representación actual.
Normalmente solo devuelves como eco la cadena ETag exacta que recibiste — la comparación débil simplemente significa que una etiqueta con prefijo W/ de tu cliente HTTP sigue coincidiendo.
Cosas a tener en cuenta
Sección titulada «Cosas a tener en cuenta»- Trata el
ETagcomo opaco. Es un hash — no lo analices, compáralo solo por igualdad, almacénalo tal cual. - Un
304sigue contando como una solicitud frente a tu límite de tasa y tu uso. Las solicitudes condicionales ahorran ancho de banda, no tu presupuesto de solicitudes — así que mantén un intervalo de sondeo sensato. - El
ETages por representación. Parámetros de consulta diferentes (por ejemplo, unproject_iddistinto, un cursor de página o un filtro) producen cuerpos diferentes y, por tanto,ETags diferentes. Almacénalos en caché por URL completa. - Las solicitudes condicionales se aplican solo a las lecturas (
GET). Las escrituras (POST/PATCH/DELETE) nunca son condicionales aquí — para reintentos seguros de escrituras, usa claves de idempotencia en su lugar.