Taking the edge off

Demystifying edge computing using Cloudflare Workers

About

Alexander Baygeldin

Backend engineer at Evil Martians

github.com/baygeldin

evl.ms/jobs

What is edge computing exactly?

serverless

+

emphasis on data locality

=

edge computing

(kind of)

BENEFIT #1

reduced latency ⚡️

Where does it matter?

  • healthcare
  • self-driving cars
  • AI assistants 🤔
BENEFIT #2

saving bandwidth 📉

Where does it matter?

  • IoT (Internet of Things)
  • smart content delivery
INTERLUDE

cutting

through the hype of

edge

computing

MPA 😎

(good ol' days)

MPA → SPA 😬

MPA → SPA → SSR 😵‍💫

MPA → SPA → SSR →

MPA? 👀

dynamic content

at the speed comparable to

static content

🚀🚀🚀

BENEFIT #3

DX 🤩

What can we do with this?

  • advanced caching
  • personalized content
  • A/B testing

www.playbook.com

EXAMPLE #1

SSR cache

Problem slow SSR responses
Solution serve the CSR version on initial request and cache the SSR version on the edge in the background

EXAMPLE #2

Canary-like deployment

Problem rolling out for everyone is too risky
Solution route part of the user traffic to the canary instance using custom logic on the edge

EXAMPLE #3

Replacing Nginx

Problem Nginx 🌚
Solution Cloudflare Workers!

nginx.conf

double-edged sword

(data consistency 😓)

Cloudflare Workers

Storage options

in-memory ad-hoc caching between requests
Cache API a bit more flexible CDN caching
KV eventually consistent key-value storage
D1 distributed SQLite database
R2 S3-like blob storage
Durable Objects strong consistency and real-time coordination
Cloudflare Workers

Features

  • no cold starts (0ms worldwide)
  • cheap ($0.50 per million requests)
  • WebAssembly support
  • Cron Triggers support
  • WebSockets support
Cloudflare Workers

DEMO

wrangler.toml

name = "example"
main = "index.js"
compatibility_date = "2023-08-14"

account_id = "<your Cloudflare account ID>"

routes = [
  {
    pattern = "https://www.example.com/*",
    zone_name = "example.com"
  }
]

index.js

export default {
  async fetch(request, env, ctx) {
    return fetch(request)
  }
}

LET'S GOOO!

  1. install Wrangler
  2. $ wrangler login
  3. $ wrangler deploy

index.js

export default {
  async fetch(request, env, ctx) {
    const url = new URL(request.url)

    if (url.pathname === '/hello') {
      return new Response('Hello World!')
    }

    return fetch(request)
  }
}
const geo = request.cf || {}

let content = ''

content += `Colo: ${geo.colo}`
content += `Country: ${geo.country}`
content += `City: ${geo.city}`
content += `Latitude: ${geo.latitude}`
content += `Longitude: ${geo.longitude}`
content += `Timezone: ${geo.timezone}`

return new Response(content, {
  headers: {
    'content-type': 'text/plain;charset=UTF-8'
  }
})
const rewriter = new HTMLRewriter().on('span#caption', {
  async element (element) {
    element.setInnerContent('hello world')
  }
})

const response = await fetch(request, {
  headers: {
    'Content-Type': 'text/html;charset=UTF-8'
  }
})

return rewriter.transform(response)
async element (element) {
  const code = request.cf?.country || 'GB'

  const url = new URL(`${env.HELLO_API_URL}/?cc=${code}`)

  const response = await fetch(url)
  const data = await response.json()

  const hello = data.hello.toLowerCase()

  element.setInnerContent(`${hello} world`)
}
async element (element) {
  const code = request.cf?.country || 'GB'

  let hello = await env.KV.get(`${code}-hello`)

  if (!hello) {
    const url = new URL(`${env.HELLO_API_URL}/?cc=${code}`)

    const response = await fetch(url)
    const data = await response.json()

    hello = data.hello.toLowerCase()

    ctx.waitUntil(env.KV.put(`${code}-hello`, hello))
  }

  element.setInnerContent(`${hello} world`)
}
Cloudflare Workers

Starter pack

  • Router (Hono)
  • Linter (Standard)
  • Tests (Jest & Miniflare)
  • TypeScript

THE EDGE END

Check out the repo!

github.com/baygeldin/lisbon-meetup-examples

 

 

 

Questions? Contact me!