Edge runtime vs serverless: what changes for full-stack TypeScript
Cold starts, the request lifecycle, and why the classic edge tradeoffs - databases, state, long-running work - don't have to apply anymore.
Five years ago "serverless" was a clear pitch: stop running servers, pay only for the requests that hit your code, scale to zero. The trade was cold starts and a Lambda-shaped execution model that didn't quite match how anyone actually wrote a backend.
"Edge runtime" sounds like the same idea with a different sticker. It isn't - the execution model is genuinely different. And most of the historical reasons to avoid edge (databases, durable state, long workflows) have been solved at the runtime layer in the last two years. For full-stack TypeScript, the calculus has changed.
What people mean by "edge runtime"
An edge runtime executes your code in dozens or hundreds of locations, close to the user, on top of a V8 isolate (or a similar lightweight sandbox) rather than a containerized Node.js process. Cloudflare Workers, Vercel Edge Functions, Deno Deploy, and Bun's edge story all share this shape.
Three things follow from that:
- Cold starts measured in single-digit milliseconds. Isolates start ~100× faster than container-based serverless because there's no Linux process, no Node.js boot, no
require()tree to evaluate. - A Web-standard runtime, not a Node runtime. You get
fetch,Request,Response,crypto.subtle,ReadableStream. Most of the Node API is missing or polyfilled. - Execution close to the user, not close to the database. This is the assumption that used to break things - and the one that modern runtimes have done the most to fix.
The cold-start math, plainly
Traditional serverless cold starts are dominated by container provisioning and language runtime boot. A typical Lambda handler in Node.js cold-starts in 200–800ms before your code even runs. An isolate-based edge worker is in the request handler in 5–20ms.
That gap is why edge platforms can keep your code at every region without keeping anything warm. There's no idle cost to absorb, so there's no incentive to consolidate traffic into a few regions and pay the latency tax.
What changes when you write the backend
The Web-standard runtime is the part that bites first. A typical Node import like:
import fs from "node:fs/promises";
import { spawn } from "node:child_process";...will either fail to bundle or compile and crash at runtime. Most edge platforms ship a compatibility layer for the safer Node modules (fs, crypto, path, stream), but anything that needs a real OS - spawning processes, watching files, opening raw TCP sockets - is out.
The upside is that the surface you do get is the same surface as the browser. A TypeScript codebase can share more code across frontend and backend. fetch works the same way. Request and Response are the function I/O, not an Express-flavored polyfill of them.
The database problem - and why it's mostly fixed now
The classic objection: a request handled in Frankfurt that does six round-trips to a Postgres database in us-east-1 will feel slower than the same handler running in us-east-1 directly. True, if you're writing against raw Postgres from raw Workers.
Three things have changed that calculus:
- Edge connection pooling for Postgres. Services like Neon, Hyperdrive, and Supabase Pooler keep a warm pool of Postgres connections at the edge and short-circuit the TLS + auth handshake on every request. A SELECT from Frankfurt no longer pays the full transatlantic tax - it pays it once, then reuses the pinned connection.
- Read caching at the edge. The same systems can cache read queries at the POP. Most application traffic is reads of the same handful of rows; caching them next to the user turns the database round-trip into a memory lookup for the hot path.
- Durable, single-instance state. Stateful primitives like Durable Objects and Vy's actors (a typed actor model with built-in storage) give you a place to put state that has to be coordinated - counters, sessions, multiplayer rooms, per-tenant queues - without standing up a separate Redis or pinning everything to one region's primary.
The patterns that work on the edge today look less like "carefully avoid the database" and more like normal application code, with the runtime handling the locality problem.
Edge isn't "serverless but better." It's a different deployment shape - and the runtime layer has caught up to the patterns it was missing two years ago.
What about long-running work?
The other historical edge weakness: anything that needs to outlive a single request. Webhooks that fan out, payment flows with retries, multi-step ingestion pipelines, video encoding jobs. Isolates have CPU time limits; you can't run a 90-second job inside a request handler.
The answer isn't "go back to long-running containers." It's workflows - durable, step-by-step execution where every step is persisted, retried on failure, and resumable across deploys. Each step is still a short edge invocation. The orchestrator is what's durable, not the process.
Combined with queues and cron, this covers almost all of what people used to reach for a long-running container to do.
When edge wins, when traditional serverless still wins
Edge is the right default when:
- Cold-start latency matters (low-traffic apps, AI agents, webhooks)
- You're serving global users from the same backend
- Your database has an edge-friendly access path (Postgres via a pooled/cached layer, or KV)
- You want one TypeScript runtime across frontend, backend, and AI agent code
- You can model long-running work as a durable workflow rather than a long process
Traditional serverless or a long-running container still wins when:
- You depend on Node-only libraries that won't run in an isolate (e.g. native bindings, headless browsers)
- You need raw, persistent socket connections to legacy systems at huge scale
- You're locked into a single-region data plane that has no edge access path at all
That list is shorter than it used to be, and it keeps getting shorter.
Where Vy fits
Vy is the runtime for full-stack TypeScript built around this thesis. You declare services, actors, Postgres databases, queues, workflows, and cron jobs in TypeScript - Vy provisions and wires them. Postgres is managed and pooled at the edge, so a query from a request handler in any region is fast by default. Actors give you durable single-instance state without standing up Redis. Workflows handle the multi-step, retry-heavy work that doesn't fit in a single request. Vy Cloud deploys the whole graph globally with per-request billing, end-to-end traces, and a live environment for every branch.
The point isn't "edge for its own sake." It's that the historical reasons to avoid the edge runtime model - databases, state, long-running work - are now problems the runtime can solve, instead of problems the application has to route around.
Build on a framework that's made for this.
Vy is the TypeScript framework for full-stack edge apps.