The press · AI Agent Development · filed 2026-06-01 · updated 2026-07-10
Edge-First Architecture: Building Commerce on Cloudflare Workers
Long-form landing article. Six primitives walkthrough for commerce on Workers, KV, D1, R2, Durable Objects, Wrangler. Voice register developer-business.
The problem
Every millisecond matters in commerce. A customer in Sydney adding an item to their cart should not wait 280ms for a round trip to a server in Virginia. A payment confirmation in São Paulo should not depend on the health of a single data center in Frankfurt. An idempotency check that prevents a duplicate charge should execute at the same location where the request arrived — not after a cross-continental hop. Origin-first architectures funnel every cart mutation, every payment intent, every mandate verification back to one region. Static assets cache at the edge. Compute does not. For static content, that is tolerable. For commerce, where every interaction mutates state, it is the bottleneck.
Cloudflare Workers run on V8 isolates across more than 300 cities in over 100 countries. Cold start is under 5 milliseconds (compared to 100ms–1s for container functions). When a request arrives, it executes in the same data center where it was received. There is no origin to route to. The edge is the application. The infrastructure cost target for a typical small commerce build runs at $0 on free tiers across Workers, KV, D1, R2, and Durable Objects.
This walks through the architecture for building commerce on that stack: the V8 isolate execution model, KV for session and idempotency, D1 for relational data, R2 for assets, Durable Objects for real-time coordination, and the Wrangler deployment pattern that ships globally with one command.
What most people get wrong
Mistake one: treating Workers like Lambda. Workers are not containers. They are not lightweight VMs. They are V8 isolates — the same JavaScript engine that powers Chrome, stripped of the DOM and filesystem, optimized for one thing: process a request, return a response. A Lambda function spins up an entire Node.js process per invocation. A Worker shares a V8 process across thousands of isolates. The implications are not just performance (sub-5ms cold start) — they are architectural. Workers have a 10ms CPU limit per request on the standard plan, 128MB memory, no filesystem, no long-running connections. Code that assumes a connection pool, a 30-second response budget, or local-disk caching breaks on Workers.
The architectural answer is the binding model. Workers do not connect to external services with connection strings. Every resource — KV, D1, R2, Durable Objects, secrets — is declared in wrangler.toml and injected into the env parameter. The same code runs locally and in production with only the binding targets changing.
interface Env {
SESSION_STORE: KVNamespace;
IDEMPOTENCY_CACHE: KVNamespace;
MANDATE_DB: D1Database;
PRODUCT_ASSETS: R2Bucket;
CART_COORDINATOR: DurableObjectNamespace;
STRIPE_SECRET_KEY: string;
ENVIRONMENT: string;
}
Zero configuration drift between environments. Connection management happens inside Cloudflare’s network. Your code calls a method on a binding and Cloudflare handles the routing.
Mistake two: putting strongly-consistent state in KV. KV is eventually consistent. Writes propagate globally in under 60 seconds. Reads serve from the nearest edge. This is exactly what you want for session tokens, idempotency-key caching, and rate-limiting counters where last-write-wins is fine. It is exactly wrong for a shopping-cart inventory counter, a live auction bid, or anything where “the value must be correct right now everywhere.” For those, you need Durable Objects — a single authoritative instance with strong consistency, in-memory state, optional SQLite persistence, and native WebSocket support. Picking the wrong primitive shows up as overselling in inventory, double-bidding in auctions, or impossible-to-debug race conditions.
This article is the short version — Edge-First Architecture: Building Commerce on Cloudflare Workers is the full playbook.
Get the ebook — $19A working approach
Six primitives. Each does one thing:
- Workers — compute. 10ms CPU, 128MB memory, V8 isolate per request, sub-5ms cold start.
- KV — eventually consistent key-value store. Sessions, idempotency, rate-limit counters.
- D1 — SQLite at the edge. Relational data, transactions, queries.
- R2 — object storage with zero egress fees. Product assets, uploads, signed URLs.
- Durable Objects — single-instance strongly consistent state. Real-time coordination, WebSockets, inventory locks.
- Wrangler — the toolchain. Deploy with one command, hot reload local dev, canary rollouts.
A 24-hour idempotency cache in KV is the canonical pattern. Every payment write is wrapped:
async function processPayment(
request: Request,
env: Env
): Promise<Response> {
const idempotencyKey = request.headers.get('Idempotency-Key');
if (!idempotencyKey) {
return new Response('Missing Idempotency-Key', { status: 400 });
}
// Check cache — if present, return cached response
const cached = await env.IDEMPOTENCY_CACHE.get(idempotencyKey, 'text');
if (cached) {
return new Response(cached, {
status: 200,
headers: { 'Content-Type': 'application/json', 'X-Idempotent': 'replay' }
});
}
// Process payment (real work)
const payment = await chargeCard(request, env);
const responseBody = JSON.stringify(payment);
// Cache result with 24h TTL
await env.IDEMPOTENCY_CACHE.put(idempotencyKey, responseBody, {
expirationTtl: 86_400
});
return new Response(responseBody, {
status: 200,
headers: { 'Content-Type': 'application/json' }
});
}
A retry of the same payment with the same idempotency key returns the cached response. No double-charge. The window TTL is 24 hours.
D1 is SQLite at the edge — full SQL, prepared statements, transactional batches:
const mandates = env.MANDATE_DB
.prepare(`
SELECT id, user_id, max_amount, status
FROM intent_mandates
WHERE user_id = ? AND status = ?
ORDER BY created_at DESC
LIMIT 10
`)
.bind(userId, 'active')
.all();
R2 holds the product images and the user-uploaded receipts. Zero egress fees mean a 10MB asset served a million times costs the same as serving it once — a margin difference that compounds on consumer commerce.
Durable Objects are where strongly-consistent state lives. A cart coordinator routes every mutation for a given cart to the same DO instance globally:
export class CartCoordinator {
constructor(private state: DurableObjectState, private env: Env) {}
async fetch(request: Request): Promise<Response> {
const { items, qty } = await request.json();
// Strongly-consistent inventory check
const stock = await this.state.storage.get<number>(items[0].sku);
if ((stock ?? 0) < qty) {
return new Response('Out of stock', { status: 409 });
}
// Atomic decrement
await this.state.storage.put(items[0].sku, (stock ?? 0) - qty);
return new Response('OK', { status: 200 });
}
}
A WebSocket coordinator can keep a thousand connections open inside one Durable Object and broadcast cart updates to all of them.
Deployment is one command:
wrangler deploy
The same wrangler.toml declares the bindings; the command pushes the Worker globally. Canary rollouts use wrangler versions upload and wrangler versions deploy NEW@5 OLD@95 to step from 5% → 25% → 50% → 100% with auto-rollback on error rate spikes.
This article is the short version — Edge-First Architecture: Building Commerce on Cloudflare Workers is the full playbook.
Get the ebook — $19Where this scales
The walkthrough above is the architecture spine. The full book covers six more dimensions:
- Sliding-window rate limiting in KV — the algorithm that survives bursty traffic without blowing the free-tier quotas, including the math for choosing the window granularity.
- D1 migrations at the edge — schema evolution, the EXPAND-ONLY rule for canary safety, and the migration patterns that survive deploy-while-traffic-runs.
- R2 signed URLs and image pipelines — secure asset access, the Cloudflare Images workflow for resizing, and the upload pattern that keeps R2 cheap at consumer scale.
- Durable Object SQLite storage — when to use the new SQLite-backed DOs, the latency profile, and the pattern for inventory locks that combines DO consistency with KV speed.
- Multi-Worker architecture — service bindings between Workers, the gateway pattern for federated APIs, and how to keep the system observable when compute is distributed across many small services.
- Performance optimization — cold-start mitigation tactics, response caching strategies, request coalescing, and the monitoring shape that catches a 99th-percentile regression before users do.
Every code example has been tested against the production Cloudflare Workers runtime as of 2026. The sub-10ms response times, the $0 infrastructure cost, the 100% uptime across 300+ locations — these are operational numbers from the soft.house API, not estimates.
Included with the book
wrangler-project-starter.toml— a production-shapedwrangler.tomlwith all the bindings declared (KV, D1, R2, Durable Objects, secrets), the canary deployment config, and the local-dev persistence settings. Drop into a new repo and replace the namespace IDs.
Get the full picture
Edge-First Architecture: Building Commerce on Cloudflare Workers — everything this article compresses, worked through end to end.
Get the ebook — $19Readers of this also chose
Questions readers ask
Can I really run a commerce platform on the free tier?
Yes, up to meaningful scale. The Workers free tier allows 100,000 requests per day. KV free covers 100K reads / 1K writes per day with 1GB storage. D1 free covers 5M reads / 100K writes per day. R2 free covers 10GB storage and zero egress. For a small commerce platform doing hundreds to a few thousand transactions per day, the entire stack fits inside free. You pay only when traffic exceeds those thresholds.
What's the catch with the 10ms CPU limit?
It is per-request wall CPU time. Network calls, KV reads, D1 queries, and subrequests don't count — only your code's actual CPU. Most commerce handlers complete in 1–3ms of CPU. If you find yourself approaching 10ms, you are usually doing too much work in one Worker and should split into multiple Workers with service bindings.
Where does Supabase fit if D1 is at the edge?
Pragma.Vision uses both. D1 holds edge-local data (session caches, simple queries, derived counters). Supabase holds the canonical operational data — users, mandates, payments — with RLS. Workers talk to Supabase via REST or via a private network connection from a dedicated region. The pattern is "edge-first reads, origin-of-truth writes for security-critical data." The book covers when to pick each.
How does authentication work in this architecture?
Sessions live in KV (with TTL matching the session lifetime). Auth tokens are validated on every Worker request — the validation is sub-1ms because it is an edge-local KV read. For OAuth, the access token gets validated against the upstream provider once at sign-in and the resulting session token is cached in KV. The auth chain is described in detail in Chapter 3.
What's the refund policy?
Lemon Squeezy's standard refund window applies. The refund link is in the receipt email.