Ionosphere.tv
3
fork

Configure Feed

Select the types of activity you want to include in your feed.

perf: add Cache-Control headers to list endpoints

Browser was re-fetching 940KB concordance, 2MB talks list, etc. on
every page load. Now all list endpoints return Cache-Control headers:
1h max-age with stale-while-revalidate for instant repeat loads.

Applies to: getTalks, getSpeakers, getConcepts, getDiscussion,
getConceptClusters, getConcordance.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

+7
+7
apps/ionosphere-appview/src/routes.ts
··· 25 25 app.get("/health", (c) => c.json({ status: "ok" })); 26 26 27 27 app.get("/xrpc/tv.ionosphere.getTalks", (c) => { 28 + c.header("Cache-Control", "public, max-age=3600, stale-while-revalidate=86400"); 28 29 // Exclude 'document' column — it's the full faceted transcript (up to 2MB per talk) 29 30 // and not needed for list views. Individual talks are fetched via getTalk. 30 31 const talks = db ··· 144 145 }); 145 146 146 147 app.get("/xrpc/tv.ionosphere.getSpeakers", (c) => { 148 + c.header("Cache-Control", "public, max-age=3600, stale-while-revalidate=86400"); 147 149 const speakers = db 148 150 .prepare("SELECT * FROM speakers ORDER BY name ASC") 149 151 .all(); ··· 171 173 }); 172 174 173 175 app.get("/xrpc/tv.ionosphere.getConcepts", (c) => { 176 + c.header("Cache-Control", "public, max-age=3600, stale-while-revalidate=86400"); 174 177 const concepts = db 175 178 .prepare("SELECT * FROM concepts ORDER BY name ASC") 176 179 .all(); ··· 265 268 }); 266 269 267 270 app.get("/xrpc/tv.ionosphere.getDiscussion", (c) => { 271 + c.header("Cache-Control", "public, max-age=3600, stale-while-revalidate=86400"); 268 272 const BLOCKED_HANDLES = new Set(['nowbreezing.ntw.app']); 269 273 const filterBlocked = (rows: any[]) => rows.filter((r: any) => !BLOCKED_HANDLES.has(r.author_handle)); 270 274 ··· 385 389 }); 386 390 387 391 app.get("/xrpc/tv.ionosphere.getConceptClusters", (c) => { 392 + c.header("Cache-Control", "public, max-age=3600, stale-while-revalidate=86400"); 388 393 try { 389 394 const clustersPath = path.resolve(import.meta.dirname, "../data/concept-clusters.json"); 390 395 const data = JSON.parse(readFileSync(clustersPath, "utf-8")); ··· 534 539 let indexCache: { entries: any[]; builtAt: number } | null = null; 535 540 536 541 app.get("/xrpc/tv.ionosphere.getConcordance", (c) => { 542 + c.header("Cache-Control", "public, max-age=3600, stale-while-revalidate=86400"); 543 + 537 544 // Serve from cache if available 538 545 if (indexCache) { 539 546 return c.json({ entries: indexCache.entries });