Edge Computing patterns: Cloudflare Workers (Durable Objects, KV, D1, R2, Wrangler), Vercel Edge Middleware (geo-routing, A/B testing, Edge Config), Deno Deploy, edge runtime constraints (no fs/child_process, bundle limits), streaming responses, edge caching (Cache API, stale-while-revalidate), and testing with Miniflare.
From clarcnpx claudepluginhub marvinrichter/clarc --plugin clarcThis skill uses the workspace's default tool permissions.
Designs and optimizes AI agent action spaces, tool definitions, observation formats, error recovery, and context for higher task completion rates.
Enables AI agents to execute x402 payments with per-task budgets, spending controls, and non-custodial wallets via MCP tools. Use when agents pay for APIs, services, or other agents.
Compares coding agents like Claude Code and Aider on custom YAML-defined codebase tasks using git worktrees, measuring pass rate, cost, time, and consistency.
Reference for building on edge runtimes — Cloudflare Workers, Vercel Edge, and Deno Deploy.
Edge runtimes execute in V8 isolates — not Node.js. Key differences:
| Feature | Node.js | Edge Runtime |
|---|---|---|
| File system | fs module | ❌ Not available |
| Child processes | child_process | ❌ Not available |
| TCP sockets | net, dgram | ❌ Not available |
| Native modules | node-gyp bindings | ❌ Not available |
| npm packages | All | Only Web-API compatible |
| Bundle size | Unlimited | Cloudflare: 1MB / Vercel: 4MB |
| CPU time | Unlimited | Cloudflare: 50ms / Vercel: 25s wall |
| Cold starts | Seconds | Milliseconds (isolate reuse) |
Available Web Standard APIs:
fetch, Request, Response, HeadersURL, URLSearchParamscrypto.subtle (Web Crypto API)ReadableStream, WritableStream, TransformStreamTextEncoder, TextDecodersetTimeout, setInterval (limited in Workers)Blob, FormData// src/index.ts
export default {
async fetch(
request: Request,
env: Env,
ctx: ExecutionContext
): Promise<Response> {
// env — bound resources (KV, D1, R2, secrets)
// ctx — lifecycle methods (waitUntil, passThroughOnException)
const url = new URL(request.url);
if (url.pathname === '/api/health') {
return Response.json({ status: 'ok' });
}
return new Response('Not Found', { status: 404 });
},
};
// Type bindings (generated by wrangler)
interface Env {
MY_KV: KVNamespace;
MY_DB: D1Database;
MY_BUCKET: R2Bucket;
MY_SECRET: string; // from wrangler.toml [vars] or Secrets
}
name = "my-worker"
main = "src/index.ts"
compatibility_date = "2024-01-01"
[[kv_namespaces]]
binding = "MY_KV"
id = "abc123"
preview_id = "def456"
[[d1_databases]]
binding = "MY_DB"
database_name = "my-db"
database_id = "uuid-here"
[[r2_buckets]]
binding = "MY_BUCKET"
bucket_name = "my-bucket"
[vars]
ENVIRONMENT = "production"
// Write
await env.MY_KV.put('user:123', JSON.stringify(user), {
expirationTtl: 3600, // seconds
});
// Read
const raw = await env.MY_KV.get('user:123');
const user = raw ? JSON.parse(raw) : null;
// Read as JSON (shorthand)
const user = await env.MY_KV.get<User>('user:123', 'json');
// Delete
await env.MY_KV.delete('user:123');
// List keys
const { keys, list_complete } = await env.MY_KV.list({ prefix: 'user:' });
// Query
const { results } = await env.MY_DB.prepare(
'SELECT * FROM users WHERE id = ?'
).bind(userId).all<User>();
// Insert
const { success } = await env.MY_DB.prepare(
'INSERT INTO users (name, email) VALUES (?, ?)'
).bind(name, email).run();
// Batch (transactional)
const [create, notify] = await env.MY_DB.batch([
env.MY_DB.prepare('INSERT INTO users (name) VALUES (?)').bind(name),
env.MY_DB.prepare('INSERT INTO events (type, user) VALUES (?, ?)').bind('created', name),
]);
// Upload
await env.MY_BUCKET.put(key, request.body, {
httpMetadata: { contentType: 'image/png' },
customMetadata: { uploadedBy: userId },
});
// Download
const object = await env.MY_BUCKET.get(key);
if (!object) return new Response('Not Found', { status: 404 });
return new Response(object.body, {
headers: {
'Content-Type': object.httpMetadata?.contentType ?? 'application/octet-stream',
'ETag': object.etag,
},
});
// Durable Object class — co-located storage + compute
export class SessionRoom {
private state: DurableObjectState;
private sessions: Map<string, WebSocket> = new Map();
constructor(state: DurableObjectState, env: Env) {
this.state = state;
// Hibernatable WebSocket API — don't hold connections during idle
this.state.acceptWebSocket(/* ... */);
}
async fetch(request: Request): Promise<Response> {
const { pathname } = new URL(request.url);
if (pathname === '/websocket') {
const pair = new WebSocketPair();
this.state.acceptWebSocket(pair[1]);
return new Response(null, { status: 101, webSocket: pair[0] });
}
// Durable storage — persists across invocations
const count = (await this.state.storage.get<number>('count') ?? 0) + 1;
await this.state.storage.put('count', count);
return Response.json({ count });
}
webSocketMessage(ws: WebSocket, message: string) {
// Broadcast to all connected clients
for (const session of this.state.getWebSockets()) {
session.send(message);
}
}
}
// wrangler.toml
// [[durable_objects.bindings]]
// name = "SESSION_ROOM"
// class_name = "SessionRoom"
export default {
async fetch(request: Request, env: Env, ctx: ExecutionContext) {
const response = await handleRequest(request, env);
// Run after response is sent — doesn't block the response
ctx.waitUntil(logAnalytics(request, response, env));
ctx.waitUntil(updateCache(request, env));
return response;
},
};
# Local dev (runs in Workerd, same runtime as production)
wrangler dev
# Deploy to production
wrangler deploy
# Deploy to staging
wrangler deploy --env staging
# Tail production logs
wrangler tail
# D1 migrations
wrangler d1 migrations create my-db add-users-table
wrangler d1 migrations apply my-db
# KV operations
wrangler kv key put --binding=MY_KV "key" "value"
wrangler kv key get --binding=MY_KV "key"
// middleware.ts — runs before every matched request
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
export function middleware(request: NextRequest) {
const { pathname, searchParams } = request.nextUrl;
// Geo-based routing
const country = request.geo?.country ?? 'US';
if (pathname === '/pricing' && country === 'DE') {
return NextResponse.rewrite(new URL('/pricing/eu', request.url));
}
// A/B testing
const bucket = request.cookies.get('ab-bucket')?.value
?? (Math.random() < 0.5 ? 'a' : 'b');
const response = NextResponse.next();
response.cookies.set('ab-bucket', bucket, { maxAge: 86400 });
if (bucket === 'b') {
response.headers.set('x-experiment', 'variant-b');
}
return response;
}
// Only run middleware on these paths
export const config = {
matcher: [
'/pricing/:path*',
'/checkout/:path*',
'/((?!api|_next|static|favicon).*)',
],
};
import { get } from '@vercel/edge-config';
export async function middleware(request: NextRequest) {
const maintenanceMode = await get<boolean>('maintenance_mode');
if (maintenanceMode) {
return NextResponse.rewrite(new URL('/maintenance', request.url));
}
return NextResponse.next();
}
# Update without deployment
vercel env pull .env.local
vercel edge-config update '{"maintenance_mode": true}'
// main.ts — Deno-native edge function
Deno.serve(async (req) => {
const url = new URL(req.url);
if (url.pathname === '/api/hello') {
return Response.json({ message: 'Hello from Deno Deploy!' });
}
return new Response('Not Found', { status: 404 });
});
const kv = await Deno.openKv();
// Set
await kv.set(['users', userId], { name, email }, { expireIn: 3600_000 });
// Get
const entry = await kv.get<User>(['users', userId]);
const user = entry.value;
// Atomic operation
const result = await kv.atomic()
.check({ key: ['counter'], versionstamp: null })
.set(['counter'], 0)
.commit();
name: Deploy to Deno Deploy
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
permissions:
id-token: write
contents: read
steps:
- uses: actions/checkout@v4
- uses: denoland/deployctl@v1
with:
project: my-deno-project
entrypoint: main.ts
For streaming responses (TransformStream, Server-Sent Events), edge caching (Cache API, stale-while-revalidate), Miniflare testing, and common mistakes, see skill edge-patterns-advanced.
serverless-patterns — Cold starts, Step Functions, Lambda Powertools, idempotencydeployment-patterns — Docker, CI/CD, rollback strategieswasm-patterns — Run Rust WASM in Cloudflare Workers