Todo producto de analytics te pide hacer una concesión. Google Analytics es gratis pero envía los datos de tus visitantes a Google y te obliga a poner un banner de cookies. Plausible y Fathom respetan la privacidad pero cuestan dinero y agregan un script. Cloudflare Analytics Engine está en otra categoría completamente distinta: corre dentro de tu Worker, escribe datos en el edge, y nunca toca el cliente.
Este sitio registra cada page view a través de él. Sin cookies, sin JavaScript del lado del cliente, sin banner de consentimiento — y todo son como seis líneas de código. Pero hay un detalle arquitectónico que te va a hacer tropezar si no lo sabés de entrada, y los docs lo mencionan casi de pasada.
El binding
Analytics Engine es un binding del Worker, declarado en wrangler.toml:
toml[[analytics_engine_datasets]]
binding = "ANALYTICS"
dataset = "ANALYTICS"
Eso te da env.ANALYTICS dentro del Worker. El dataset se crea implícitamente la primera vez que escribís en él — no hay paso de aprovisionamiento, ni toggle en el dashboard.
Escribir un data point
Cada request de HTML en este sitio dispara una sola escritura:
javascriptenv.ANALYTICS.writeDataPoint({
blobs: [path, request.headers.get("Referer") || "", request.headers.get("User-Agent") || ""],
doubles: [1],
indexes: [path],
});
El esquema es raro a primera vista. No definís columnas — llenás tres arrays tipados:
blobs— strings, hasta 20 por data point. Acá: el path, el referer, el user agent.doubles— números, hasta 20. Acá: un1literal, para poder hacerlesSUM()y obtener un conteo de vistas después.indexes— un único string usado como clave de sampling (más sobre sampling abajo).
El modelo de datos es posicional, no nombrado. blob1 es el path, blob2 es el referer, y así. Tenés que recordar el orden vos mismo — no se guardan nombres de campo en ningún lado. Yo dejo un comentario al lado de la llamada para que mi yo del futuro no tenga que hacer ingeniería inversa.
Fire and forget — pero nunca rompas la página
La escritura va envuelta en un try/catch que se traga todo:
javascripttry {
env.ANALYTICS.writeDataPoint({ blobs: [...], doubles: [1], indexes: [path] });
} catch(e) {}
writeDataPoint() no devuelve una promesa — es síncrono y no bloqueante por diseño, bufferizado y enviado por el runtime después de que la respuesta sale. Pero el binding puede ser undefined en algunos contextos (dev local sin el flag, un environment mal configurado), y una llamada de telemetría nunca debería ser lo que tumbe una página. Analytics es el byte menos importante de la respuesta. Si lanza un error, el visitante nunca debería enterarse.
La parte que los docs entierran: el binding es solo de escritura
Acá está el detalle que me sorprendió. Desde adentro de un Worker, env.ANALYTICS solo puede escribir. Hay un método writeDataPoint() y esa es toda la superficie de la API. No hay query(), no hay read(), no hay forma de preguntar "¿cuántas vistas tuvo este post?" desde el mismo Worker que las registró.
Si lo intentás, obtenés undefined is not a function. El binding es una tubería de una sola dirección.
Para leer tus propios datos, usás un canal completamente separado: la API de SQL sobre HTTPS.
bashcurl "https://api.cloudflare.com/client/v4/accounts/{account_id}/analytics_engine/sql" \
-H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \
-d "SELECT blob1 AS path, SUM(_sample_interval) AS views
FROM ANALYTICS
WHERE timestamp > NOW() - INTERVAL '7' DAY
GROUP BY path
ORDER BY views DESC"
Esto significa que leer analytics requiere un API token guardado como secret — no un binding. El mismo Worker que escribe los datos no puede leerlos de vuelta sin hacer una llamada HTTP externa autenticada a la API de Cloudflare. Para una página /status que quiere mostrar tráfico en vivo, eso es un round trip extra y una credencial que administrar, así que en este sitio ahí muestro conteos de vistas respaldados por D1 y dejo Analytics Engine para el dashboard.
Sampling: por qué hacés SUM de _sample_interval, no COUNT(*)
Esta es la segunda cosa que muerde a la gente. A alto volumen, Analytics Engine hace sampling — no guarda cada data point. Para obtener una estimación real de conteos, no hacés COUNT(*); hacés SUM(_sample_interval).
Cada fila guardada lleva una columna _sample_interval que representa cuántos eventos reales está sustituyendo. Si una fila fue muestreada a 1-de-10, su _sample_interval es 10. Sumar esos intervalos reconstruye el total real; contar filas subestimaría por el factor de sampling.
El campo indexes que poneés al escribir es la clave de sampling — Analytics Engine muestrea por valor de índice, así que índices de alta cardinalidad (como un ID de request único) anulan el propósito. Usar el path como índice mantiene el sampling coherente por página.
Consistencia eventual
Los datos no son en tiempo real. Las escrituras tardan hasta unos minutos en volverse consultables, y los docs de Cloudflare notan que el dashboard puede ir ~30 minutos atrás. Para un dashboard de tráfico esto está bien. Para cualquier cosa que necesite leer de vuelta un valor que recién escribió — un contador en vivo, un rate limiter — Analytics Engine es la herramienta equivocada. Para eso están KV y D1. Analytics Engine es para preguntas agregadas respondidas después del hecho: qué posts son populares, de dónde viene el tráfico, cómo evoluciona.
Por qué le gana a analytics basado en cookies
La historia de privacidad no es un ángulo de marketing — es una consecuencia estructural de dónde corre el código:
- Sin cookies. El visitante nunca queda etiquetado. No hay nada que consentir, así que no hay banner.
- Sin JavaScript del cliente. Nada que cargar, nada que bloquear, nada que se rompa bajo un Content-Security-Policy estricto. La medición ocurre del lado del servidor antes de que la respuesta salga.
- Sin terceros. Los datos nunca dejan el edge de Cloudflare. No estás alimentando a una red publicitaria.
- Sin PII por defecto. Guardo path, referer y user agent — sin IP, sin fingerprint, sin identificador que siga a una persona entre requests.
La concesión es real: perdés el tracking de sesión por usuario, los funnels, y cualquier cosa que requiera identificar a un visitante entre visitas. Para un blog personal, eso no es una pérdida — es el punto entero. Quiero saber qué posts lee la gente, no quién es la gente.
Cuándo recurrir a él
Analytics Engine es la herramienta correcta cuando querés telemetría agregada de alto volumen y pesada en escritura desde adentro de un Worker y estás dispuesto a consultarla por fuera de banda. Page views, conteos de llamadas a API, sampling de tasa de errores, hit rates de feature flags — cualquier cosa que vayas a rebanar y sumar después, no leer de vuelta de inmediato.
Es la herramienta equivocada cuando necesitás leer un valor de forma síncrona, cuando necesitás conteos exactos (sin sampling) a bajo volumen, o cuando necesitás los datos en el instante en que se escriben. Para eso, el binding de solo escritura y el modelo de sampling son exactamente las restricciones que lo hacen barato y rápido para lo que sí es bueno.