Edge & Serverless Runtimes: Workers, Vercel & WinterTC
Here is a number that quietly shapes every architecture decision you make: light travels about 200 kilometers per millisecond through fiber. A request from Sydney to a server in Virginia covers roughly 16,000 km each way. Even in a perfect world with zero processing, that round trip costs you around 160 milliseconds — and the real world is never perfect. Add TLS handshakes, congestion, and a few hops, and a single distant server can add a quarter-second to every interaction before your code has done anything at all.
You have already met the server-side runtimes — Node, Deno, Bun — and how to deploy an app built on them. This article is about a different axis: not which runtime, but where your code runs and how it is scheduled. That axis is where “serverless” and “the edge” live, and it is where the ecosystem has spent the last few years converging on a shared, web-standard way to write portable server code.
Servers you keep vs functions you rent
The traditional model is a long-running server. You provision a machine (or a container), start a Node process, and it sits there — holding open database pools, warm caches, in-memory state — answering requests until you stop it. You pay for that machine whether it handles ten requests an hour or ten thousand a second. Idle time is wasted money; a traffic spike beyond its capacity is dropped requests.
Serverless flips the billing and the lifecycle. You upload a function, and the platform runs an instance of it only when a request arrives, then bills you for the milliseconds it actually executed. No traffic, no running instances, no bill. That is the appeal: you stop paying for idle, and the platform scales instances up and down for you.
Nothing is free. Serverless hands you two constraints in exchange. Functions are stateless — anything you kept in memory is gone when the instance is torn down, so session data, caches, and connections have to live somewhere external. And the first request to a fresh instance pays a cold start: the time to allocate a sandbox, load your code, and initialize it before your handler runs. Keep an instance warm with steady traffic and cold starts vanish; let it go quiet and the next visitor eats the boot cost.
The size of that cold start is the whole game, and it depends entirely on what the platform boots. That is where isolates change the math.
The edge: run close to the user
“The edge” means running your code not in one data center but across a fleet of hundreds of locations worldwide, so a request is served from whichever one is physically nearest the user. Cloudflare’s network spans 330+ cities; the point of a request from Sydney is that it lands in Sydney, not Virginia.
Latency is not an abstraction here — it compounds. A page that makes three sequential API calls pays that round trip three times. Move the logic to the edge and each call shrinks from ~160ms to ~10ms. Play with it below: pick a user’s region and toggle between an edge deployment and a single origin in Virginia. The numbers are illustrative, but the shape is real — the origin’s cost scales with distance while the edge stays flat.
Why isolates start faster than containers
Not all serverless is edge, and not all cold starts are equal. The difference comes down to the unit of isolation — the wall the platform puts around your code so it can’t touch anyone else’s.
Most serverless platforms — classic AWS Lambda, for instance — isolate each function in its own container or micro-VM. That is a genuine, private OS environment: its own filesystem, its own process, its own copy of the Node runtime. It is strong isolation, but booting one is not cheap. Allocating the sandbox, starting the runtime, and loading your bundle takes anywhere from tens of milliseconds to well over a second for a heavy function. That is your cold start.
Cloudflare Workers (and the platforms built on the same idea) use V8 isolates instead. An isolate is a lightweight sandbox inside a single, already-running V8 process — the same mechanism that keeps two browser tabs from reading each other’s memory. Because the process is already warm, spinning up a new isolate is not booting a machine; it is closer to creating an object. The runtime is loaded once and shared; only your code and its heap are new. That is how Workers advertises cold starts under 5 milliseconds — often low enough that the isolate is created during the TLS handshake and effectively disappears.
The platforms, as of mid-2026
The space moves fast, so here is an honest snapshot rather than a ranking.
- Cloudflare Workers is the reference isolate platform: sub-5ms cold starts across 330+ locations, running on the open-source
workerdruntime. The paid plan starts at $5/month with 10 million requests included; a free tier exists for hobby use. Workers pairs with a family of storage bindings (KV, R2, D1, Durable Objects) covered below. - Vercel Functions consolidated its serverless story under fluid compute. The standalone Edge Functions product was retired in mid-2025; the web-standard Edge runtime now lives inside Vercel Functions, and fluid compute lets multiple invocations share one warm instance — which is what makes it good at I/O-heavy and AI workloads and largely dissolves cold starts on sustained traffic. Fluid is the default for new projects.
- Deno Deploy was rebuilt and reached general availability. It auto-detects your framework, runs the right build, and ships to a global network; the free plan includes roughly 1 million requests a month. Note the migration deadline: the older Deno Deploy Classic and the subhosting v1 API shut down on July 20, 2026, so new work should target the current platform.
- AWS Lambda remains the container-model incumbent, and it is where LLRT (Low Latency Runtime) is worth knowing about. LLRT is an experimental AWS runtime built in Rust on the QuickJS engine, aimed at drastically faster startup and lower cost for Lambda by shipping a tiny engine instead of full V8. It is explicitly experimental and not recommended for production yet — a signal of where the cost pressure is pushing, not a tool to bet a launch on.
WinterTC: one API surface everywhere
Here is the problem all of this created. Each runtime grew its own APIs. Node reads a request with http.IncomingMessage; the browser has fetch and Request/Response; early Workers and Deno each did their own thing. Code written for one did not run on another, and “portable server code” was a fantasy.
The fix was to agree on a shared vocabulary — and the obvious candidate was the set of APIs the browser already standardized. fetch, Request, Response, URL, URLSearchParams, Headers, Web Streams, TextEncoder, structuredClone, crypto.subtle. These are web standards with years of specification behind them. If every server runtime exposed the same web APIs, code that used only those would run unchanged everywhere.
That effort started as WinterCG, a W3C community group. In early 2025 it graduated into an Ecma International technical committee, TC55, branded WinterTC. Its deliverable is the Minimum Common Web API — the concrete list of web APIs a compliant server runtime must expose. The first edition (the 2025 snapshot) was adopted by Ecma’s General Assembly in December 2025. It is a real standard now, not just a shared aspiration.
In practice this is why the modern way to write a request handler looks the same everywhere. You get a Request, you return a Response:
// This handler is web-standard. It runs on Workers, Deno, Bun,
// and Node (via an adapter) without changing a line.
export default {
async fetch(request) {
const url = new URL(request.url);
if (url.pathname === "/health") {
return new Response("ok", { status: 200 });
}
const body = JSON.stringify({ path: url.pathname, when: Date.now() });
return new Response(body, {
headers: { "content-type": "application/json" },
});
},
};
Hono: the framework for the shared surface
A raw fetch handler with a big if on url.pathname gets old fast. You want routing, middleware, params, and typed responses — the things Express gave Node. The catch is that Express is built on Node’s http objects, so it does not run on Workers or Deno.
Hono is what filled that gap. It is a small (~14KB), zero-dependency web framework built entirely on the WinterTC surface. Because it only touches web-standard APIs, one Hono app runs unchanged on Cloudflare Workers, Deno, Bun, Node, AWS Lambda, Vercel, Netlify, and Fastly Compute — you swap the adapter, not the app. As of April 2026 it sits around v4.12, is written in TypeScript from the first line, and ships a typed RPC client and JSX out of the box. It has quietly become the default for edge apps.
The API will feel familiar if you know Express, with one big difference: the request and response are the web-standard objects, and the context object (c) wraps them.
import { Hono } from "hono";
const app = new Hono();
app.get("/", (c) => c.text("Hello from the edge"));
app.get("/users/:id", (c) => {
const id = c.req.param("id");
return c.json({ id, name: "Ada" });
});
// Middleware is just a function that runs before the handler.
app.use("/admin/*", async (c, next) => {
if (c.req.header("authorization") !== "Bearer secret") {
return c.text("Unauthorized", 401);
}
await next();
});
export default app;
That same app deploys to wildly different targets with only the entry glue changing:
// Node: wrap it in the Node server adapter.
import { serve } from "@hono/node-server";
import app from "./app";
serve({ fetch: app.fetch, port: 3000 });
// Deno: Deno.serve speaks the web-standard handler directly.
import app from "./app.ts";
Deno.serve(app.fetch);
On Cloudflare Workers you export default app and let wrangler deploy it; on Bun you pass app.fetch to Bun.serve. The business logic — routes, middleware, validation — never moves.
Living inside the smaller box
The edge’s speed comes from what it takes away. Before you move something there, know the walls.
- No filesystem, no long-lived state. An isolate has no disk you can write to and no memory that survives between requests. Reading a config file at boot, writing an upload to
/tmp, caching in a module-levelMapand expecting it next time — none of that works. State lives in a binding (below) or an external service. - No native Node built-ins by default.
fs,net,child_process, most ofos— absent. Some runtimes offer partialnode:compatibility shims, and Workers has a growing Node-compat layer, but if a dependency reaches deep into Node internals it may simply not run. Libraries built on the web-standard surface (like Hono) sidestep this entirely. - CPU and wall-clock limits. Edge functions cap the CPU time a single request may burn — you get a slice, not a whole core for as long as you like. A tight image-resize loop or a heavy crypto grind can blow the budget. These platforms are tuned for short, I/O-bound work: authenticate, transform, fetch, respond. Long-running batch jobs belong elsewhere.
Edge, regional, or origin — choosing
Not everything belongs at the edge. The right question is where does the data live and how much compute does this need, and the answer sorts into three tiers.
A worked example: a request comes in for a personalized dashboard. The edge checks the auth cookie and, if it is missing, redirects — no reason to cross an ocean to learn the user is logged out. A valid request needs live data, so it hits a regional function sitting next to the primary database, where ten quick queries cost ten short local round trips instead of ten transoceanic ones. And the nightly job that re-renders every user’s report as a PDF? That is heavy, long, and native-dependency-laden — it belongs on an origin server or a queue worker, nowhere near a per-request budget. Latency-sensitive and light goes out; data-heavy stays near the data; long and specialized stays home.
Bindings, KV, D1 — state at the edge
If an isolate has no disk and no durable memory, how does an edge app store anything? Through bindings — declared connections from your Worker to a platform resource, injected into your handler rather than reached over the public network. On Workers you list them in wrangler.jsonc and they arrive on the env argument. The common ones:
- KV — a globally distributed key-value store, tuned for read-heavy, eventually-consistent data: feature flags, config, cached HTML. Reads are fast everywhere; writes take a moment to propagate.
- R2 — S3-compatible object storage for large blobs (images, uploads, backups), with no egress fees.
- D1 — a SQLite database you query with real SQL, for relational data that fits the edge model.
- Durable Objects — the exception to “stateless”: a single addressable object with its own consistent storage, for coordination like a chat room, a game lobby, or a rate limiter that must see every request in order.
// wrangler.jsonc declares the binding; env.SESSIONS is a KV namespace.
app.get("/me", async (c) => {
const token = c.req.header("authorization")?.replace("Bearer ", "");
if (!token) return c.json({ error: "no token" }, 401);
const session = await c.env.SESSIONS.get(token, "json");
if (!session) return c.json({ error: "expired" }, 401);
return c.json({ user: session.user });
});
The mental shift from a traditional server is that there is no single database connection you open at startup and hold. Each binding is a handle the platform hands you per request, already routed to the resource. You reach for the right storage primitive per workload — KV for fast global reads, D1 for relational queries, R2 for blobs, a Durable Object when you truly need one consistent owner of some state — rather than defaulting to one big relational database for everything.
Summary
- Serverless trades always-on servers for per-request instances: no idle cost and automatic scale, at the price of statelessness and cold starts.
- The edge runs your code across hundreds of locations so requests are served near the user, turning ~160ms round trips into ~10ms ones. Edge is placement; serverless is lifecycle — they are separate axes.
- V8 isolates cold-start in single-digit milliseconds because they are lightweight sandboxes inside one already-warm process, versus containers that boot a whole OS and runtime. Cloudflare Workers advertises sub-5ms starts across 330+ locations.
- The platforms as of mid-2026: Workers (isolate reference), Vercel Functions with fluid compute (Edge Functions retired in 2025), Deno Deploy (GA; Classic shuts down July 20, 2026), and AWS Lambda plus the experimental LLRT.
- WinterTC (Ecma TC55, formerly WinterCG) standardized the Minimum Common Web API —
fetch,Request/Response,URL, Web Streams and friends — first edition adopted December 2025. Stay inside that surface and your code is portable across Node, Deno, Bun and Workers. - Hono is the cross-runtime framework built on that surface: an Express-shaped API in ~14KB that deploys unchanged to every major runtime by swapping only the entry adapter.
- The edge takes away the filesystem, durable memory, native Node built-ins, and long CPU time. Store state in bindings (KV, R2, D1, Durable Objects), do outside-world work inside the handler, and send heavy or data-chatty work to a regional or origin tier instead.