Skip to content

Instantly share code, notes, and snippets.

@njbotkin
Last active December 19, 2019 13:25
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save njbotkin/9a170999e23fb34d4113634a6aba47b0 to your computer and use it in GitHub Desktop.
Save njbotkin/9a170999e23fb34d4113634a6aba47b0 to your computer and use it in GitHub Desktop.
Trigger SPA reloads without server polling with Cloudflare Workers

How it works:

  1. On app deploy, autoincrement Cloudflare KV value
  2. Cloudflare Worker adds a "version" cookie to every Response with the content from the KV
  3. Client checks cookies periodically to see if the "version" cookie has changed. If so, reload page
// bump version in Cloudflare KV
const fetch = require('node-fetch')
const vars = require('./cloudflare.var.js')
async function api(method, body) {
let res = await fetch(`https://api.cloudflare.com/client/v4/accounts/${vars.CLOUDFLARE_ACCOUNT_ID}/storage/kv/namespaces/${vars.CLOUDFLARE_VERSION_NAMESPACE_ID}/values/version`, {
method,
headers: {
"X-Auth-Email": vars.CLOUDFLARE_AUTH_EMAIL,
"X-Auth-Key": vars.CLOUDFLARE_AUTH_KEY,
},
body,
})
return await res.text()
}
;(async () => await api(`PUT`, (+await api(`GET`) || 0) + 1))()
// watch version cookie and reload
const cookies_to_object = cookies => cookies.split(';').reduce((a, c) => {
let [k, v] = c.split('=')
a[k.trim()] = v
return a
}, {})
let previous_cookie = document.cookie
let { version } = cookies_to_object(document.cookie)
setInterval(function() {
if(previous_cookie == document.cookie) return
if(cookies_to_object(document.cookie).version !== version) location.reload()
previous_cookie = document.cookie
}, 100)
// instead of .env
module.exports = {
"CLOUDFLARE_AUTH_KEY": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"CLOUDFLARE_AUTH_EMAIL": "xxxxx@xxxxx.com",
"CLOUDFLARE_ACCOUNT_ID": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"CLOUDFLARE_ZONE_ID": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"CLOUDFLARE_VERSION_NAMESPACE_ID": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
}
// cloudflare worker
/* global CLOUDFLARE_NAMESPACE */
async function handleRequest(request) {
try {
let response = await fetch(request)
let cookie = response.headers.get('Set-Cookie')
response = new Response(response.body, response)
const version = await CLOUDFLARE_NAMESPACE.get('version')
response.headers.set("Set-Cookie", `${cookie ? cookie+'; ' : ''}version=${version};path=/`)
return response
} catch(e) {
return new Response(e.toString())
}
}
addEventListener('fetch', event => {
event.respondWith(handleRequest(event.request))
})
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment