The traditional serverless model served us well for years, but as our platform scaled to handle tens of thousands of concurrent users across multiple continents, the limitations became painful. Cold starts on AWS Lambda were adding 200-800ms to critical API paths. Regional deployments meant users in Asia were routing to US-East origins. The answer was moving compute to the edge with Cloudflare Workers.
Why the Edge Changes Everything
Cloudflare Workers run on V8 isolates rather than containers. This distinction matters more than most teams realize. There are no cold starts because there are no containers to spin up. Your code is already running at over 300 data centers worldwide when a request arrives. For our event platform serving Fortune 500 clients, this meant API response times dropped from 180ms average to under 12ms at the P99 level.
The programming model is also fundamentally different from traditional serverless:
export default {
async fetch(request, env, ctx) {
const url = new URL(request.url);
// KV for fast reads, D1 for relational data, R2 for assets
const cachedResponse = await env.CACHE_KV.get(url.pathname, 'json');
if (cachedResponse) {
return Response.json(cachedResponse, {
headers: { 'X-Cache': 'HIT', 'X-Edge-Location': request.cf?.colo }
});
}
const data = await env.DB.prepare(
'SELECT * FROM events WHERE slug = ?'
).bind(url.pathname.split('/').pop()).first();
ctx.waitUntil(
env.CACHE_KV.put(url.pathname, JSON.stringify(data), { expirationTtl: 300 })
);
return Response.json(data);
}
};
What makes this powerful is ctx.waitUntil. You return the response immediately to the user while background work like cache population, analytics writes, and webhook dispatches happen asynchronously. The user never waits for non-critical operations.
The Migration Strategy
We did not rewrite everything at once. The approach was to start with the read-heavy paths that benefited most from edge caching: event listings, speaker profiles, and schedule data. These endpoints accounted for roughly 70% of our total API traffic but were largely static between content updates.
The key architectural decision was using Cloudflare KV as a global read cache with D1 as the relational backing store. KV replicates to every edge location eventually, meaning a write in one region propagates globally within about 60 seconds. For our use case where event data changed infrequently but was read millions of times, this was ideal. We paired it with a webhook from our CMS that invalidated specific KV keys on content updates, keeping cache coherence tight.
Lessons Learned at Scale
Running at the edge introduces constraints you would not encounter in a traditional Node.js server. The Worker runtime has a 128MB memory limit and a 30-second CPU time limit per request. You cannot use Node.js native modules. These constraints forced us to write leaner code, which turned out to be a net positive. Our bundle went from a 45MB Lambda deployment to a 1.2MB Worker script.
The biggest surprise was how much simpler our infrastructure became. We eliminated our CloudFront distribution, our API Gateway configuration, our Lambda functions, and the associated IAM roles. The entire edge layer is defined in a single wrangler.toml and deployed with wrangler deploy. Our infrastructure cost dropped by 60% while latency improved by an order of magnitude. For teams building latency-sensitive applications serving a global audience, the edge is no longer optional.