Back to Blog
Edge ComputingCDNPerformanceArchitecture

Edge Computing for Web Developers: A Practical Introduction

Understand edge computing and how it changes web development. From edge functions to CDN capabilities to deployment strategies.

B
Bootspring Team
Engineering
June 15, 2025
6 min read

Edge computing brings computation closer to users—running code at CDN edge locations worldwide rather than centralized data centers. This fundamental shift enables new performance and personalization capabilities for web applications.

What Is Edge Computing?#

Traditional Architecture#

User (Tokyo) → CDN (static) → Origin Server (US-East) → Response └── 200ms latency

Edge Architecture#

User (Tokyo) → Edge Function (Tokyo) → Response └── 20ms latency

Edge functions run at hundreds of locations globally, processing requests milliseconds from users.

Edge Platforms#

Major Providers#

Cloudflare Workers - V8 isolates - 0ms cold start - 200+ locations Vercel Edge Functions - Built on Cloudflare - Next.js integration - Edge middleware AWS Lambda@Edge / CloudFront Functions - CloudFront integration - Regional edge caches - Origin request/response Deno Deploy - Deno runtime - TypeScript native - Global deployment

Edge Function Basics#

Cloudflare Workers#

1export default { 2 async fetch(request, env, ctx) { 3 const url = new URL(request.url); 4 5 // Personalization based on location 6 const country = request.cf?.country || 'US'; 7 const greeting = getGreeting(country); 8 9 // Return response from edge 10 return new Response(JSON.stringify({ 11 greeting, 12 location: request.cf?.city, 13 timestamp: new Date().toISOString(), 14 }), { 15 headers: { 'Content-Type': 'application/json' }, 16 }); 17 }, 18}; 19 20function getGreeting(country) { 21 const greetings = { 22 US: 'Hello', 23 ES: 'Hola', 24 FR: 'Bonjour', 25 JP: 'こんにちは', 26 }; 27 return greetings[country] || 'Hello'; 28}

Vercel Edge Functions#

1// app/api/greeting/route.ts 2import { NextRequest, NextResponse } from 'next/server'; 3 4export const runtime = 'edge'; 5 6export async function GET(request: NextRequest) { 7 const country = request.geo?.country || 'US'; 8 const city = request.geo?.city || 'Unknown'; 9 10 return NextResponse.json({ 11 greeting: `Hello from ${city}, ${country}!`, 12 timestamp: Date.now(), 13 }); 14}

Common Use Cases#

Geolocation and Personalization#

1export default { 2 async fetch(request) { 3 const { country, city, timezone } = request.cf || {}; 4 5 // Currency based on country 6 const currency = getCurrency(country); 7 8 // Language preferences 9 const language = request.headers.get('Accept-Language')?.split(',')[0] || 'en'; 10 11 // Modify HTML response 12 const response = await fetch(request); 13 const html = await response.text(); 14 15 const personalizedHtml = html 16 .replace('{{CURRENCY}}', currency) 17 .replace('{{LANG}}', language) 18 .replace('{{CITY}}', city); 19 20 return new Response(personalizedHtml, { 21 headers: response.headers, 22 }); 23 }, 24};

A/B Testing#

1export default { 2 async fetch(request) { 3 const url = new URL(request.url); 4 5 // Get or create experiment assignment 6 const cookie = request.headers.get('Cookie') || ''; 7 let variant = getCookie(cookie, 'ab_variant'); 8 9 if (!variant) { 10 variant = Math.random() < 0.5 ? 'control' : 'treatment'; 11 } 12 13 // Modify request to appropriate backend 14 const backendUrl = variant === 'treatment' 15 ? 'https://new-experience.example.com' 16 : 'https://current.example.com'; 17 18 const response = await fetch(backendUrl + url.pathname); 19 20 // Set cookie for consistency 21 const newResponse = new Response(response.body, response); 22 newResponse.headers.set('Set-Cookie', 23 `ab_variant=${variant}; Path=/; Max-Age=86400`); 24 25 return newResponse; 26 }, 27};

Authentication at the Edge#

1import { verify } from '@tsndr/cloudflare-worker-jwt'; 2 3export default { 4 async fetch(request, env) { 5 const authHeader = request.headers.get('Authorization'); 6 7 if (!authHeader?.startsWith('Bearer ')) { 8 return new Response('Unauthorized', { status: 401 }); 9 } 10 11 const token = authHeader.slice(7); 12 13 try { 14 const isValid = await verify(token, env.JWT_SECRET); 15 if (!isValid) { 16 return new Response('Invalid token', { status: 401 }); 17 } 18 } catch { 19 return new Response('Invalid token', { status: 401 }); 20 } 21 22 // Token valid, forward to origin 23 return fetch(request); 24 }, 25};

Rate Limiting#

1export default { 2 async fetch(request, env) { 3 const ip = request.headers.get('CF-Connecting-IP'); 4 const key = `ratelimit:${ip}`; 5 6 // Use Cloudflare KV or Durable Objects 7 const count = parseInt(await env.RATE_LIMITS.get(key)) || 0; 8 9 if (count >= 100) { 10 return new Response('Rate limit exceeded', { 11 status: 429, 12 headers: { 'Retry-After': '60' }, 13 }); 14 } 15 16 await env.RATE_LIMITS.put(key, String(count + 1), { 17 expirationTtl: 60, 18 }); 19 20 return fetch(request); 21 }, 22};

Edge Middleware#

Next.js Middleware#

1// middleware.ts 2import { NextResponse } from 'next/server'; 3import type { NextRequest } from 'next/server'; 4 5export function middleware(request: NextRequest) { 6 // Get country from geo 7 const country = request.geo?.country || 'US'; 8 9 // Redirect to localized version 10 if (country !== 'US' && !request.nextUrl.pathname.startsWith(`/${country.toLowerCase()}`)) { 11 return NextResponse.redirect( 12 new URL(`/${country.toLowerCase()}${request.nextUrl.pathname}`, request.url) 13 ); 14 } 15 16 // Add headers 17 const response = NextResponse.next(); 18 response.headers.set('X-Country', country); 19 20 return response; 21} 22 23export const config = { 24 matcher: ['/((?!api|_next/static|favicon.ico).*)'], 25};

Data at the Edge#

Key-Value Storage#

1// Cloudflare KV 2export default { 3 async fetch(request, env) { 4 // Read 5 const value = await env.MY_KV.get('key'); 6 7 // Write 8 await env.MY_KV.put('key', 'value', { 9 expirationTtl: 3600, 10 }); 11 12 // Read with metadata 13 const { value, metadata } = await env.MY_KV.getWithMetadata('key'); 14 15 return new Response(value); 16 }, 17};

Durable Objects (Stateful Edge)#

1// Durable Object for stateful operations 2export class Counter { 3 constructor(state, env) { 4 this.state = state; 5 } 6 7 async fetch(request) { 8 let value = await this.state.storage.get('count') || 0; 9 value++; 10 await this.state.storage.put('count', value); 11 return new Response(String(value)); 12 } 13} 14 15// Worker that uses the Durable Object 16export default { 17 async fetch(request, env) { 18 const id = env.COUNTER.idFromName('global'); 19 const counter = env.COUNTER.get(id); 20 return counter.fetch(request); 21 }, 22};

Limitations and Trade-offs#

Edge Constraints#

Limited capabilities: - No filesystem access - Limited CPU time (50ms - 30s depending on plan) - Memory limits (128MB - 512MB) - No persistent connections to databases - Limited Node.js API compatibility Solutions: - Use edge-compatible libraries - Cache database results in KV - Use edge-native databases (PlanetScale, Turso) - Fallback to regional functions for complex operations

When NOT to Use Edge#

Keep at origin: - Complex database queries - Long-running operations - Heavy computation - Large file processing - Operations requiring full Node.js API Use edge for: - Routing and redirects - Authentication checks - Personalization - A/B testing - Static asset optimization - Simple API responses

Hybrid Architecture#

┌─────────────────────────────────────────────┐ │ User │ └─────────────────────┬───────────────────────┘ │ ┌─────────────────────▼───────────────────────┐ │ Edge Function │ │ - Auth check │ │ - Geo-personalization │ │ - Route decision │ └─────────────┬───────────────┬───────────────┘ │ │ ┌─────────▼─────────┐ ┌───▼────────────────┐ │ Static Assets │ │ Origin Server │ │ (CDN Cache) │ │ (Complex Logic) │ └──────────────────┘ └────────────────────┘

Deployment#

Cloudflare Workers#

1# Install Wrangler CLI 2npm install -g wrangler 3 4# Login 5wrangler login 6 7# Create project 8wrangler init my-worker 9 10# Deploy 11wrangler publish

Vercel Edge#

# Deploy with Vercel vercel # Edge functions in app/api/*/route.ts # Add: export const runtime = 'edge';

Conclusion#

Edge computing fundamentally changes web architecture—moving computation from centralized origins to distributed edge locations milliseconds from users. This enables personalization, security, and performance improvements impossible with traditional architectures.

Start with simple use cases: authentication, redirects, or personalization. As you gain experience, expand to more complex edge logic. The future of web development is increasingly at the edge.

Share this article

Help spread the word about Bootspring