Introduction
Edge computing brings computation closer to users. With latencies under 10ms globally, edge functions enable real-time personalization, A/B testing, and security processing at the network edge.
Key Statistics:
- Edge functions reduce latency by 60-80%
- Cloudflare processes requests in 50ms globally
- Edge computing market: $15B by 2027
- 40% of web traffic goes through edge functions
Edge Architecture
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Edge Computing Architecture โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ โ Global Edge Network โโ
โ โ โโโโโโโโโโ โโโโโโโโโโ โโโโโโโโโโ โโโโโโโโโโ โโโโโโโโโโ โโ
โ โ โ US-E โ โ US-W โ โ EU-C โ โ ASIA โ โ SA โ โโ
โ โ โ (NYC) โ โ (LAX) โ โ (FRA) โ โ (SIN) โ โ (GRU) โ โโ
โ โ โโโโโโโโโโ โโโโโโโโโโ โโโโโโโโโโ โโโโโโโโโโ โโโโโโโโโโ โโ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ โ โ
โ โโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโโโโ โ
โ โผ โผ โผ โ
โ โโโโโโโโโโโโโโ โโโโโโโโโโโโโโ โโโโโโโโโโโโโโ โ
โ โ A/B Test โ โ Auth/JWT โ โ Rate Limit โ โ
โ โ Personalizeโ โ Transform โ โ Security โ โ
โ โโโโโโโโโโโโโโ โโโโโโโโโโโโโโ โโโโโโโโโโโโโโ โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Cloudflare Workers
Getting Started
// index.js - Cloudflare Worker
export default {
async fetch(request, env, ctx) {
const url = new URL(request.url);
// Handle different routes
if (url.pathname === '/api/data') {
return handleApiRequest(request, env);
}
if (url.pathname.startsWith('/user/')) {
return handleUserRequest(request, env, url.pathname);
}
// Default: fetch from origin
return fetch(request);
}
};
async function handleApiRequest(request, env) {
// Parse request
const data = await request.json();
// Transform response
const response = await fetch('https://api.internal.com/data', {
method: 'POST',
body: JSON.stringify(data),
headers: {
'Authorization': `Bearer ${env.API_SECRET}`
}
});
const result = await response.json();
// Add edge-specific headers
return new Response(JSON.stringify(result), {
headers: {
'Content-Type': 'application/json',
'X-Edge-Location': 'global',
'Cache-Control': 'public, max-age=60'
}
});
}
async function handleUserRequest(request, env, path) {
const userId = path.split('/')[2];
// Fetch from KV store
const user = await env.USERS.get(userId);
if (!user) {
return new Response('User not found', { status: 404 });
}
return new Response(user, {
headers: { 'Content-Type': 'application/json' }
});
}
Durable Objects
// durable-object.js
export class UserSession {
constructor(state, env) {
this.state = state;
this.env = env;
}
async fetch(request) {
const url = new URL(request.url);
// Get or create session
let session = await this.state.get('session');
if (!session) {
session = {
id: crypto.randomUUID(),
created: Date.now(),
data: {}
};
}
if (url.pathname === '/get') {
return new Response(JSON.stringify(session), {
headers: { 'Content-Type': 'application/json' }
});
}
if (url.pathname === '/set') {
const updates = await request.json();
session.data = { ...session.data, ...updates };
session.updated = Date.now();
await this.state.put('session', session);
return new Response(JSON.stringify({ success: true }));
}
return new Response('Not found', { status: 404 });
}
}
AWS Lambda@Edge
Function Configuration
// viewer-request.js - Lambda@Edge
exports.handler = async (event, context) => {
const request = event.Records[0].cf.request;
// Add security headers
request.headers['strict-transport-security'] = [{
key: 'Strict-Transport-Security',
value: 'max-age=31536000; includeSubDomains'
}];
request.headers['x-content-type-options'] = [{
key: 'X-Content-Type-Options',
value: 'nosniff'
}];
request.headers['x-frame-options'] = [{
key: 'X-Frame-Options',
value: 'DENY'
}];
return request;
};
// origin-request.js
exports.handler = async (event, context) => {
const request = event.Records[0].cf.request;
// Add authentication
const authHeader = request.headers['authorization'];
if (!authHeader && !request.uri.startsWith('/public/')) {
return {
status: '401',
statusDescription: 'Unauthorized',
headers: {
'www-authenticate': [{
key: 'WWW-Authenticate',
value: 'Bearer realm="protected"'
}]
}
};
}
// Transform request
request.uri = request.uri.replace('/api/', '/api/v1/');
return request;
};
// origin-response.js
exports.handler = async (event, context) => {
const response = event.Records[0].cf.response;
// Add caching headers
response.headers['cache-control'] = [{
key: 'Cache-Control',
value: 'public, max-age=3600, s-maxage=86400'
}];
// Add custom header
response.headers['x-edge-function'] = [{
key: 'X-Edge-Function',
value: 'lambda@edge'
}];
return response;
};
CloudFront Trigger Setup
# Terraform Lambda@Edge
resource "aws_lambda_function" "edge_function" {
filename = "function.zip"
function_name = "edge-function"
role = aws_iam_role.edge_role.arn
handler = "index.handler"
runtime = "nodejs20.x"
timeout = 30
lifecycle {
create_before_destroy = true
}
}
resource "aws_cloudfront_distribution" "cdn" {
origin {
domain_name = "origin.example.com"
origin_id = "custom-origin"
}
default_cache_behavior {
allowed_methods = ["GET", "HEAD", "OPTIONS", "PUT", "POST", "PATCH", "DELETE"]
cached_methods = ["GET", "HEAD"]
target_origin_id = "custom-origin"
viewer_protocol_policy = "redirect-to-https"
lambda_function_association {
event_type = "viewer-request"
lambda_arn = aws_lambda_function.edge_function.arn
include_body = false
}
lambda_function_association {
event_type = "origin-request"
lambda_arn = aws_lambda_function.edge_function.arn
include_body = false
}
}
}
Edge Patterns
A/B Testing
// A/B testing at edge
export default {
async fetch(request, env, ctx) {
// Check for existing bucket assignment
let bucket = request.headers.get('x-ab-bucket');
// Assign bucket if not exists
if (!bucket) {
bucket = Math.random() < 0.5 ? 'a' : 'b';
}
// Fetch origin with bucket
const response = await fetch(request.url, {
headers: {
'X-AB-Bucket': bucket,
...request.headers
}
});
// Set cookie if not present
if (!request.headers.get('cookie')?.includes('ab_bucket')) {
const modifiedResponse = new Response(response.body, response);
modifiedResponse.headers.set(
'Set-Cookie',
`ab_bucket=${bucket}; Path=/; Max-Age=2592000; SameSite=Lax`
);
modifiedResponse.headers.set('X-Variant', bucket);
return modifiedResponse;
}
return response;
}
};
Rate Limiting
// Rate limiting with KV store
export default {
async fetch(request, env, ctx) {
const ip = request.headers.get('CF-Connecting-IP');
const limit = 100; // requests per minute
const window = 60;
const key = `rate:${ip}`;
const current = await env.RATE_LIMIT.get(key, 'json');
const now = Date.now();
if (current && current.count >= limit) {
// Check if window expired
if (now - current.window_start < window * 1000) {
return new Response('Too Many Requests', {
status: 429,
headers: {
'Retry-After': String(window),
'X-RateLimit-Remaining': '0'
}
});
}
}
// Update counter
const newCount = {
count: (current?.count || 0) + 1,
window_start: now
};
ctx.waitUntil(env.RATE_LIMIT.put(key, JSON.stringify(newCount)));
const response = await fetch(request);
response.headers.set('X-RateLimit-Remaining', String(limit - newCount.count));
return response;
}
};
Comments