Skip to content

Instantly share code, notes, and snippets.

@richardscarrott
Last active August 25, 2023 19:17
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save richardscarrott/0d54f2252d434ce90d6f743192fe4d91 to your computer and use it in GitHub Desktop.
Save richardscarrott/0d54f2252d434ce90d6f743192fe4d91 to your computer and use it in GitHub Desktop.
Cloudflare Workers / Pages `stale-while-revalidate`
import { parse } from 'cache-control-parser';
export default {
async fetch(request: Request, env: {}, ctx: ExecutionContext): Promise<Response> {
try {
const cache = await caches.default;
const cachedResponse = await cache.match(request);
if (cachedResponse) {
console.log('Cache: HIT');
if (shouldRevalidate(cachedResponse)) {
console.log('Cache: REVALIDATE');
ctx.waitUntil(handleRequest(request, env, ctx).then((response) => cache.put(request, response.clone())));
}
return cachedResponse;
}
console.log('Cache: MISS');
const response = await handleRequest(request, env, ctx);
ctx.waitUntil(cache.put(request, response.clone()));
return response;
} catch (ex) {
console.error(ex);
return new Response('Internal server error', { status: 500 });
}
},
};
// e.g. Remix handler
const handleRequest = async (request: Request, env: {}, ctx: ExecutionContext): Promise<Response> => {
const response = await fetch('https://swapi.dev/api/people/1/');
if (!response.ok) {
return new Response(JSON.stringify({ error: `${response.status}: ${response.statusText}` }), { status: 500 });
}
const result = await response.json();
await sleep(2000);
return new Response(
`
<!doctype html>
<html>
<head>
<title>Hello world</title>
</head>
<body>
<h1>Success</h1>
<div>0.0.2</div>
<div>${new Date().toString()}</div>
<pre>${JSON.stringify(result, null, 2)}</pre>
</body>
</html>
`,
{
headers: {
'content-type': 'text/html',
'cache-control': 'no-cache, no-store, must-revalidate',
'cdn-cache-control': 'max-age=14400, stale-while-revalidate=10',
},
}
);
};
const shouldRevalidate = (cachedResponse: Response) => {
const ageHeader = cachedResponse.headers.get('age');
const cacheControlHeader = cachedResponse.headers.get('cdn-cache-control');
if (!ageHeader || !cacheControlHeader) {
return false;
}
const staleWhileRevalidate = parse(cacheControlHeader)['stale-while-revalidate'];
if (typeof staleWhileRevalidate === 'undefined') {
return false;
}
const age = Number(ageHeader);
if (Number.isNaN(age)) {
return false;
}
return age > staleWhileRevalidate;
};
const sleep = (duration: number) => new Promise((resolve) => setTimeout(resolve, duration));
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment