A Next.js (App Router) project intended for deployment to Cloudflare Workers using the OpenNext adapter, with a separate cron Worker that pre-fetches and caches ticker logos so visitors never call Massive/Polygon directly.
- Massive (Polygon.io): company branding images (logo/icon) for tickers (implemented).
- Runs on a cron schedule (configured in
workers/refresh-logos-cron/wrangler.jsonc) and executes the Worker’sscheduled()handler.[8][9] - For each run, processes a small batch of tickers (currently 1 ticker per run via
MAX_TICKERS_PER_RUN = 1) using a KV cursor (cfg:cursor). - For each ticker:
- Calls Massive ticker overview endpoint to get
branding.logo_urland/orbranding.icon_url. - Downloads logo and icon (if available), chooses the smaller file, and stores the bytes in R2 (
LOGOS_CACHE) atlogos/<TICKER>.<ext>. - Stores metadata (not bytes) in KV (
LOGO_META) atticker:<TICKER>:{ key, mime, bytes, source_url, updated_at }.
- Calls Massive ticker overview endpoint to get
- Also writes operational keys into KV:
cfg:tickers(the canonical ticker list for the app)lastRunandlastRunResult(debug/status)
Worker HTTP endpoints:
- Public:
GET /logo/<TICKER>: serves the cached bytes from R2 (no Massive calls).
- Admin (Bearer token):
GET /status: showslastRun,lastRunResult, and whichticker:*keys exist.GET /meta/<TICKER>: returns the KV metadata JSON.GET /run: queues a refresh batch manually (dev/debug).
- API route
GET /api/logosreads:cfg:tickers+ticker:<TICKER>metadata from KV- corresponding image bytes from R2 using the stored
key
- It returns a JSON payload of logos as
data:URIs (base64) to keep the browser from needing direct R2 access.
- Cache ticker logos in R2 + store metadata in KV so clients never call Massive directly.
pnpm create cloudflare@latest . --framework=next
- run these in
workers/refresh-logos-cron - Run:
pnpm dlx wrangler dev --test-scheduled - Trigger:
http://localhost:8787/__scheduled(local scheduled test endpoint).[9] - Verify KV:
pnpm dlx wrangler kv key get "lastRun" --binding LOGO_META --remote --text
- R2 bucket:
logos-cache(bound asLOGOS_CACHE) - Workers KV namespace:
LOGO_META LOGO_META_preview: previously used, now disconnected / not used
MASSIVE_API_KEY=... # from Massive dashboard
ADMIN_TOKEN=... # long random bearer token for /status, /run, /meta/* (you-make-this-up-reference)LOGO_WORKER_BASE_URL=http://localhost:8787
LOGO_WORKER_ADMIN_TOKEN= # you-make-this-up-referenceNote: the current Next.js flow uses /api/logos (and reads KV/R2 directly via bindings) rather than calling the cron Worker over HTTP; these are mainly useful for debugging/admin endpoints and future expansions.
- Cron schedules run based on Cloudflare’s cron trigger configuration, and expressions are evaluated in UTC.[8]
- Type generation (for bindings) can be used to avoid TypeScript errors when referencing
KVNamespace/R2Bucketbindings.