How I Built My Data Page A developer built a personal data dashboard on Astro and Cloudflare Workers that aggregates Goodreads, Spotify, Chess.com, Steam and coding-agent token usage into five pages, using Workers KV caching with TTLs ranging from 15 minutes for token usage to 6 hours for books and Steam. Each page routes third-party API calls through a single withCache helper that skips caching empty results and falls back to a live fetch if KV fails, while pages opt out of prerendering with `export const prerender = false` and send Cache-Control headers with s-maxage and stale-while-revalidate. The project grew over roughly a year and a half, starting in February 2025 with Goodreads and Spotify pages on Vercel before moving to Cloudflare Workers. Writing / en https://thinhcorner.com/writing How I Built My Data Page I like looking at my own habits as data. How many books did I finish this year? Which artist have I had on repeat for six months without noticing? Is my chess rating actually going up, or does it only feel that way after one good night? How much would my coding agents cost if I paid for every token at API prices? The answers already exist. They are just scattered across Goodreads, Spotify, Chess.com, Steam and a pile of log files on several computers. The /data https://thinhcorner.com/data section of this site is where I pull them into one place. There are five pages right now: - Books https://thinhcorner.com/data/books : everything I have read on Goodreads, by year and month, with ratings. - Music https://thinhcorner.com/data/music : recently played tracks, plus top tracks and artists over three time ranges from Spotify. - Chess https://thinhcorner.com/data/chess : ratings, win rates, openings and recent games from Chess.com. - Steam https://thinhcorner.com/data/steam : where my gaming hours went and what I am playing now. - Token usage https://thinhcorner.com/data/token-usage : daily tokens and estimated spend across the coding agents and models I use. The shape of every page All five pages follow the same pattern, so adding a new one is mostly boring work, which is what I want. The site is built with Astro https://astro.build/ and runs on Cloudflare Workers https://workers.cloudflare.com/ . Blog posts are prerendered at build time, but every data page opts out with export const prerender = false , so it renders when someone requests it. Each service has one small module in src/lib/ that knows how to talk to that service and returns plain objects. The page only formats them. The important part sits between those two: a cache. Third-party APIs are slow, rate limited, and sometimes down. I do not want a visitor’s page load to depend on Spotify’s mood, so every fetch goes through one helper backed by Workers KV https://developers.cloudflare.com/kv/ : export async function withCache