Skip to main content
โšก Calmops

Edge Computing for Startups: Cloudflare Workers vs Vercel Edge

Introduction

Edge computing brings your code closer to users, dramatically reducing latency. For startups, edge functions offer the power of serverless with better performance. This guide compares the two leading options: Cloudflare Workers and Vercel Edge Functions.


Why Edge Computing Matters

The Problem with Traditional Serverless

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚              Traditional vs Edge                             โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚                                                             โ”‚
โ”‚  Traditional Serverless (Lambda):                          โ”‚
โ”‚                                                             โ”‚
โ”‚  User โ†’ Internet โ†’ US-East-1 Lambda โ†’ Response            โ”‚
โ”‚     โ”‚                                                    โ”‚
โ”‚     โ””โ”€ Latency: 50-200ms for distant users                โ”‚
โ”‚                                                             โ”‚
โ”‚  Edge Computing:                                           โ”‚
โ”‚                                                             โ”‚
โ”‚  User โ†’ Nearest Edge Server โ†’ Response                    โ”‚
โ”‚     โ”‚                                                    โ”‚
โ”‚     โ””โ”€ Latency: 5-20ms worldwide                          โ”‚
โ”‚                                                             โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

What Can Run on Edge

# Edge function use cases
use_cases:
  - "API proxying and request transformation"
  - "A/B testing"
  - "Geolocation-based content"
  - "Authentication verification"
  - "Rate limiting"
  - "Request validation"
  - "Static site enhancement"
  - "Real-time personalization"

Cloudflare Workers

Getting Started

# Install Wrangler (Cloudflare CLI)
npm install -g wrangler

# Create a new worker
wrangler generate my-worker

# Development
wrangler dev

# Deploy
wrangler deploy

Worker Example

// worker.js
export default {
  async fetch(request, env, ctx) {
    const url = new URL(request.url)
    
    // Route handling
    if (url.pathname === '/api/users') {
      return handleUsers(request)
    }
    
    if (url.pathname.startsWith('/api/')) {
      return handleApiProxy(request)
    }
    
    // Default: fetch from origin
    return fetch(request)
  }
}

async function handleUsers(request) {
  // Get users from database
  const users = await getUsersFromD1()
  
  return new Response(JSON.stringify(users), {
    headers: { 'Content-Type': 'application/json' }
  })
}

D1 Database (SQLite at Edge)

// Using D1 (edge database)
export default {
  async fetch(request, env, ctx) {
    const { pathname } = new URL(request.url)
    
    if (pathname === '/users') {
      // Query D1 database
      const { results } = await env.DB.prepare(
        'SELECT * FROM users LIMIT 10'
      ).all()
      
      return new Response(JSON.stringify(results))
    }
    
    return new Response('Not found', { status: 404 })
  }
}
# wrangler.toml
name = "my-app"
compatibility_date = "2024-01-01"

[[d1_databases]]
binding = "DB"
database_name = "my-database"
database_id = "abc-123"

KV for Fast Storage

// Workers KV (key-value store)
export default {
  async fetch(request, env, ctx) {
    const url = new URL(request.url)
    
    // Read from KV
    if (url.pathname === '/config') {
      const config = await env.KV.get('app_config')
      return new Response(config)
    }
    
    // Write to KV
    if (url.pathname === '/set' && request.method === 'POST') {
      const body = await request.json()
      await env.KV.put(body.key, body.value)
      return new Response('OK')
    }
    
    return new Response('Not found', { status: 404 })
  }
}

Vercel Edge Functions

Getting Started

// app/api/hello/route.ts
export const runtime = 'edge'

export default function handler(request: Request) {
  return new Response(
    JSON.stringify({
      message: 'Hello from Edge!',
      location: request.headers.get('x-vercel-id') || 'unknown',
    }),
    {
      headers: { 'Content-Type': 'application/json' },
    }
  )
}

Edge Runtime Features

// Using edge-specific APIs
export const runtime = 'edge'

export default async function handler(request: Request) {
  // Geolocation data
  const country = request.geo?.country || 'US'
  const city = request.geo?.city || 'Unknown'
  
  // Personalize content based on location
  const content = {
    us: { greeting: 'Hello!', currency: 'USD' },
    uk: { greeting: 'Hello!', currency: 'GBP' },
    jp: { greeting: 'Konnichiwa!', currency: 'JPY' },
  }[country] || { greeting: 'Hello!', currency: 'USD' }
  
  return new Response(JSON.stringify({
    ...content,
    location: { country, city }
  }), {
    headers: { 'Content-Type': 'application/json' }
  })
}

Edge Config

// Instant config updates without deployment
import { EdgeConfig } from '@vercel/edge-config'

export const runtime = 'edge'

export default async function handler(request: Request) {
  // Connect to Edge Config
  const config = await EdgeConfig.connect(process.env.EDGE_CONFIG)
  
  // Get values (instantly updated)
  const featureFlags = await config.get('featureFlags')
  const abTest = await config.get('abTest')
  
  // Use config values
  const showNewFeature = featureFlags?.newDashboard
  
  return new Response(JSON.stringify({ showNewFeature, abTest }), {
    headers: { 'Content-Type': 'application/json' }
  })
}

Performance Comparison

Cold Start Times

# Cold start comparison (2025)
cloudflare_workers:
  cold_start: "5-10ms"
  min_latency: "5ms"
  global_points: "300+"
  
vercel_edge:
  cold_start: "10-20ms"
  min_latency: "10ms"
  global_points: "250+"

Pricing Comparison

# Monthly pricing (2025)
cloudflare_workers:
  free:
    requests: "10M/month"
    compute: "1M CPU seconds"
    bandwidth: "1GB"
  paid:
    requests: "$5/10M"
    compute: "$0.000625/CPU second"
    bandwidth: "$0.15/GB"
    
vercel_edge:
  free:
    requests: "100K/month"
    bandwidth: "1GB"
  paid:
    requests: "$0.000019/request"
    bandwidth: "$0.15/GB"

When to Choose

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚                  Edge Platform Selection                     โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚                                                             โ”‚
โ”‚  Choose CLOUDFLARE WORKERS if:                            โ”‚
โ”‚  โ€ข Need D1 (SQLite at edge)                               โ”‚
โ”‚  โ€ข Want R2 for storage (no egress fees)                   โ”‚
โ”‚  โ€ข Need Workers KV for caching                            โ”‚
โ”‚  โ€ข Budget is critical (generous free tier)                โ”‚
โ”‚  โ€ข Building complex edge applications                     โ”‚
โ”‚                                                             โ”‚
โ”‚  Choose VERCEL EDGE if:                                   โ”‚
โ”‚  โ€ข Already using Next.js                                  โ”‚
โ”‚  โ€ข Want simplest DX                                       โ”‚
โ”‚  โ€ข Need Edge Config for feature flags                     โ”‚
โ”‚  โ€ข Deploying to existing Vercel projects                  โ”‚
โ”‚  โ€ข Prefer TypeScript-first experience                     โ”‚
โ”‚                                                             โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Practical Examples

A/B Testing at Edge

// Cloudflare Workers - A/B Testing
export default {
  async fetch(request, env, ctx) {
    // Get cookie or create new bucket
    let bucket = request.headers.get('x-ab-bucket')
    
    if (!bucket) {
      bucket = Math.random() < 0.5 ? 'a' : 'b'
    }
    
    // Fetch variant content
    const variant = bucket === 'a' 
      ? await fetch('https://example.com/variant-a.html')
      : await fetch('https://example.com/variant-b.html')
    
    // Return with cookie
    const response = new Response(variant.body, variant)
    response.headers.set('Set-Cookie', `ab-bucket=${bucket}; Path=/; Max-Age=2592000`)
    
    return response
  }
}

Request Validation

// Vercel Edge - Request Validation
export const runtime = 'edge'

export default async function handler(request: Request) {
  // Validate request before processing
  const { searchParams } = new URL(request.url)
  const apiKey = request.headers.get('x-api-key')
  
  // Verify API key
  if (!apiKey || !await validateKey(apiKey)) {
    return new Response('Unauthorized', { status: 401 })
  }
  
  // Rate limit check
  const ip = request.headers.get('x-forwarded-for') || 'unknown'
  if (await isRateLimited(ip)) {
    return new Response('Too Many Requests', { status: 429 })
  }
  
  // Process request
  return new Response('OK')
}

Geolocation Redirect

// Cloudflare Workers - Geo Redirect
export default {
  async fetch(request, env, ctx) {
    const country = request.headers.get('cf-ipcountry')
    const url = new URL(request.url)
    
    // Redirect based on country
    if (country === 'JP' && !url.pathname.startsWith('/jp')) {
      return Response.redirect('https://example.jp' + url.pathname, 302)
    }
    
    if (country === 'DE' && !url.pathname.startsWith('/de')) {
      return Response.redirect('https://example.de' + url.pathname, 302)
    }
    
    return fetch(request)
  }
}

Common Pitfalls

1. Not Using Prepared Statements

Wrong:

// SQL injection vulnerable
const query = `SELECT * FROM users WHERE id = ${userId}`

Correct:

// Parameterized query
const { results } = await env.DB.prepare(
  'SELECT * FROM users WHERE id = ?'
).bind(userId).all()

2. Blocking Operations

Wrong:

// Synchronous file read blocks
const data = fs.readFileSync('/path/to/file')

Correct:

// Async operations only
const data = await readFile('/path/to/file')

Key Takeaways

  • Edge = Faster - 5-20ms vs 50-200ms latency
  • Cloudflare Workers - Best free tier, D1 database, R2 storage
  • Vercel Edge - Best if already using Next.js
  • Both are free at small scale - Great for startups
  • Use for: auth, redirects, A/B testing, personalization

External Resources

Documentation

Tools

Pricing

Comments