| name | infrastructure-cloudflare-workers |
| description | Building serverless APIs at the edge (low latency globally) |
Cloudflare Workers
Scope: Edge computing with Cloudflare Workers - Edge functions, KV storage, Durable Objects, R2
Lines: 365
Last Updated: 2025-10-18
Format Version: 1.0 (Atomic)
When to Use This Skill
Activate when:
- Building serverless APIs at the edge (low latency globally)
- Implementing CDN logic and custom routing
- A/B testing and feature flags
- Authentication and authorization at the edge
- API proxying and transformation
- Static site generation with dynamic elements
Prerequisites:
- Cloudflare account (free tier available)
- Node.js installed for Wrangler CLI
- Wrangler CLI installed (
npm install -g wrangler)
- Basic JavaScript/TypeScript knowledge
- Cloudflare domain (for production deployment)
Common scenarios:
- API endpoints with global low latency
- Image resizing and optimization
- Request/response manipulation
- Rate limiting and bot protection
- Geo-routing and localization
- Edge-side rendering (ESR)
Core Concepts
1. Basic Worker
export default {
async fetch(request, env, ctx) {
const url = new URL(request.url);
if (url.pathname === '/api/hello') {
return new Response(JSON.stringify({
message: 'Hello from the edge!',
location: request.cf.city,
country: request.cf.country
}), {
headers: {
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': '*'
}
});
}
return new Response('Not Found', { status: 404 });
}
};
2. Wrangler Configuration
name = "my-worker"
main = "src/index.js"
compatibility_date = "2024-01-01"
[vars]
ENVIRONMENT = "production"
API_VERSION = "v1"
[[kv_namespaces]]
binding = "CACHE"
id = "abcdef1234567890abcdef1234567890"
[[durable_objects.bindings]]
name = "COUNTER"
class_name = "Counter"
[[r2_buckets]]
binding = "ASSETS"
bucket_name = "my-assets"
[[routes]]
pattern = "api.example.com/*"
zone_name = "example.com"
[dispatch_namespaces]
binding = "DISPATCHER"
namespace = "my-namespace"
3. Request/Response Handling
interface Env {
CACHE: KVNamespace;
API_KEY: string;
ENVIRONMENT: string;
}
export default {
async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
const url = new URL(request.url);
if (request.method === 'OPTIONS') {
return handleCORS();
}
const apiKey = request.headers.get('X-API-Key');
if (apiKey !== env.API_KEY) {
return new Response('Unauthorized', { status: 401 });
}
switch (url.pathname) {
case '/api/data':
return handleData(request, env, ctx);
:
(request, env);
:
(, { : });
}
}
};
(): {
(, {
: {
: ,
: ,
: ,
: ,
}
});
}
(): <> {
cacheKey = (request.).;
cached = env..(cacheKey, );
(cached) {
.(cached, {
: { : }
});
}
data = ();
ctx.(
env..(cacheKey, .(data), {
:
})
);
.(data, {
: { : }
});
}
() {
response = ();
response.();
}
4. KV Storage
interface Env {
CACHE: KVNamespace;
}
export default {
async fetch(request: Request, env: Env): Promise<Response> {
const url = new URL(request.url);
const key = url.searchParams.get('key');
if (request.method === 'GET') {
const value = await env.CACHE.get(key);
if (!value) {
return new Response('Not Found', { status: 404 });
}
return new Response(value);
}
if (request.method === 'POST') {
const value = await request.text();
await env.CACHE.put(key, value, {
expirationTtl: ,
: { : .() }
});
(, { : });
}
(request. === ) {
env..(key);
(, { : });
}
(url. === ) {
prefix = url..() || ;
list = env..({ prefix, : });
.(list.);
}
(, { : });
}
};
5. Durable Objects
export class Counter {
state: DurableObjectState;
value: number;
constructor(state: DurableObjectState, env: Env) {
this.state = state;
this.value = 0;
}
async initialize() {
const stored = await this.state.storage.get('value');
this.value = stored || 0;
}
async fetch(request: Request): Promise<Response> {
await this.initialize();
const url = new URL(request.url);
if (url.pathname === '/increment') {
this.value++;
await ...(, .);
.({ : . });
}
(url. === ) {
.--;
...(, .);
.({ : . });
}
(url. === ) {
.({ : . });
}
(, { : });
}
}
{ } ;
{
: ;
}
{
(: , : ): <> {
id = env..();
stub = env..(id);
stub.(request);
}
};
6. R2 Storage
interface Env {
ASSETS: R2Bucket;
}
export default {
async fetch(request: Request, env: Env): Promise<Response> {
const url = new URL(request.url);
const key = url.pathname.slice(1);
if (request.method === 'GET') {
const object = await env.ASSETS.get(key);
if (!object) {
return new Response('Not Found', { status: 404 });
}
const headers = new Headers();
object.writeHttpMetadata(headers);
headers.set('etag', object.httpEtag);
return new Response(., { headers });
}
(request. === ) {
env..(key, request., {
: {
: request..() || ,
},
: {
: ,
: ().(),
}
});
(, { : });
}
(request. === ) {
env..(key);
(, { : });
}
(, { : });
}
};
Patterns
Edge Caching
export default {
async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
const cache = caches.default;
let response = await cache.match(request);
if (response) {
return response;
}
response = await fetch(request);
if (response.ok) {
const responseToCache = response.clone();
ctx.waitUntil(cache.put(request, responseToCache));
}
return response;
}
};
Request Rewriting
export default {
async fetch(request: Request): Promise<Response> {
const url = new URL(request.url);
if (url.pathname.startsWith('/api/')) {
url.hostname = 'api.backend.com';
url.pathname = url.pathname.replace('/api/', '/v1/');
const modifiedRequest = new Request(url.toString(), {
method: request.method,
headers: request.headers,
body: request.body,
});
return fetch(modifiedRequest);
}
url.hostname = 'static.example.com';
return fetch(url.toString());
}
};
A/B Testing
export default {
async fetch(request: Request): Promise<Response> {
const url = new URL(request.url);
let variant = getCookie(request, 'ab_variant');
if (!variant) {
variant = Math.random() < 0.5 ? 'A' : 'B';
}
const response = await fetch(`https://origin.com/${variant}${url.pathname}`);
const newResponse = new Response(response.body, response);
newResponse.headers.set('Set-Cookie', `ab_variant=${variant}; Path=/; Max-Age=86400`);
return newResponse;
}
};
function getCookie(request: Request, name: string): string | {
cookies = request..();
(!cookies) ;
cookie = cookies.().( c.().());
cookie ? cookie.()[] : ;
}
Rate Limiting
export class RateLimiter {
state: DurableObjectState;
requests: Map<string, number[]>;
constructor(state: DurableObjectState) {
this.state = state;
this.requests = new Map();
}
async fetch(request: Request): Promise<Response> {
const clientId = request.headers.get('CF-Connecting-IP') || 'unknown';
const now = Date.now();
const windowMs = 60000;
const maxRequests = 100;
let timestamps = this.requests.get(clientId) || [];
timestamps = timestamps.filter(ts => now - ts < windowMs);
(timestamps. >= maxRequests) {
(, {
: ,
: {
:
}
});
}
timestamps.(now);
..(clientId, timestamps);
.({
: ,
: maxRequests - timestamps.
});
}
}
Image Resizing
export default {
async fetch(request: Request): Promise<Response> {
const url = new URL(request.url);
const width = parseInt(url.searchParams.get('width') || '800');
const quality = parseInt(url.searchParams.get('quality') || '85');
const format = url.searchParams.get('format') || 'auto';
const imageUrl = url.searchParams.get('url');
if (!imageUrl) {
return new Response('Missing url parameter', { status: 400 });
}
const response = await fetch(imageUrl);
return new Response(response.body, {
: {
: ,
: ,
: width.(),
: quality.(),
: format,
}
});
}
};
Quick Reference
Wrangler Commands
wrangler dev
wrangler dev --remote
wrangler deploy
wrangler deploy --env staging
wrangler kv:namespace create "CACHE"
wrangler kv:key put --binding=CACHE "key" "value"
wrangler kv:key get --binding=CACHE "key"
wrangler kv:key delete --binding=CACHE "key"
wrangler kv:key list --binding=CACHE
wrangler r2 bucket create my-bucket
wrangler r2 object put my-bucket/file.txt --file=./file.txt
wrangler r2 object get my-bucket/file.txt
wrangler secret put API_KEY
wrangler secret delete API_KEY
wrangler secret list
wrangler tail
wrangler tail --format=pretty
wrangler whoami
wrangler deployments list
Worker Limits
CPU Time: 10ms (free), 30s (paid) per request
Memory: 128 MB
Subrequest limit: 50 (free), 1000 (paid)
Script size: 1 MB (free), 10 MB (paid)
KV read: 100,000/day (free), unlimited (paid)
KV write: 1,000/day (free), unlimited (paid)
Durable Objects: Paid feature only
R2 storage: 10 GB (free), unlimited (paid)
Anti-Patterns
Critical Violations
const API_KEY = "sk-1234567890abcdef";
interface Env {
API_KEY: string;
}
export default {
async fetch(request: Request, env: Env): Promise<Response> {
if (request.headers.get('X-API-Key') !== env.API_KEY) {
return new Response('Unauthorized', { status: 401 });
}
}
};
export default {
async fetch(request: Request): Promise<Response> {
let result = 0;
for (let i = 0; i < 1000000000; i++) {
result += i;
}
return Response.json({ result });
}
};
let counter = 0;
export default {
async fetch(request: Request): Promise<Response> {
counter++;
return Response.json({ counter });
}
};
interface Env {
CACHE: KVNamespace;
}
export default {
async fetch(request: Request, env: Env): Promise<Response> {
const counter = parseInt(await env.CACHE.get('counter') || '0');
await env.CACHE.put('counter', (counter + 1).toString());
return Response.json({ counter: counter + 1 });
}
};
Common Mistakes
export default {
async fetch(request: Request): Promise<Response> {
return Response.json({ data: 'value' });
}
};
export default {
async fetch(request: Request): Promise<Response> {
const headers = {
'Access-Control-Allow-Origin': '*',
'Content-Type': 'application/json',
};
return Response.json({ data: 'value' }, { headers });
}
};
Related Skills
Infrastructure:
aws-serverless.md - Alternative serverless platform (Lambda, API Gateway)
infrastructure-security.md - Authentication, authorization patterns
cost-optimization.md - Workers pricing, caching strategies
Development:
modal-functions-basics.md - Python-focused serverless alternative
terraform-patterns.md - Infrastructure as Code for Cloudflare resources
Standards from CLAUDE.md:
- Use wrangler CLI for all operations
- Store secrets with
wrangler secret put
- TypeScript for type safety
- Always handle CORS for API endpoints
- Use KV for simple key-value, Durable Objects for complex state
Last Updated: 2025-10-18
Format Version: 1.0 (Atomic)