What an Edge Function actually is
A Vercel Edge Function is a piece of JavaScript that runs inside a V8 isolate on Vercel's edge network — the same POPs that serve your static assets. It is not Node.js. The global APIs are Web platform APIs: fetch, Request, Response, URL, Headers, crypto.subtle, TextEncoder, ReadableStream. There is no fs module, no child_process, no native addons, and no Buffer unless you import it explicitly. Code executes in a sandbox that starts in tens of milliseconds and shuts down as soon as your response is flushed, so it behaves less like a long-lived server and more like a request-scoped worker. That constraint is what makes the runtime cheap and fast; it is also what surprises teams migrating a Node app to the edge for the first time.
Why isolates instead of containers
V8 isolates share a single host process, so they cold-start in roughly 50ms compared with 500ms–2s for a full Lambda container. Multiple functions can share memory pages, JIT caches, and TCP connections, which is why edge providers can afford to deploy your code to dozens of regions without going bankrupt. The trade-off is isolation depth: an isolate is a per-request sandbox, not a full OS process, so anything that expects a filesystem, environment inspection, or long-running background work will not fit. Think of it as a very fast function that must finish quickly and cleanly.
The 1-second init budget
Vercel enforces a 1 second CPU budget for the initial module evaluation. That includes every import you resolve at module scope. Import a heavyweight ORM, a 500KB markdown parser, or a big validation schema at the top of the file and you can trip the limit before your handler ever runs. The fix is boring but reliable: keep the top of the file to type imports and small pure utilities, and lazy-import anything expensive inside the handler with a dynamic import(). The isolate caches modules across warm invocations, so the second request pays almost nothing.
When to pick Edge over Serverless
- Auth checks and header rewrites — low latency, no DB round trip in the hot path.
- Personalization based on geo/IP — the edge already knows the visitor's region.
- A/B testing and feature flags — millisecond decisions, no cold-start tax.
- Streaming AI responses — Edge Functions natively support ReadableStream and Server-Sent Events.
- Cheap fan-out — one request that calls several downstream APIs concurrently.
When Serverless is still the right call
- Long-running work (>30s) — Edge tops out at 30s, Serverless goes to 60s+ on Pro and 900s on Enterprise.
- Large dependency graphs — no 1s init budget on Lambda, so heavy ORMs and PDF libs work fine.
- Node-only libraries — sharp, puppeteer, playwright, native database drivers, anything with a .node addon.
- Heavy CPU work — image transforms, PDF generation, big parsing jobs.
- Tight VPC access — Lambda can attach to a private network; Edge cannot.
A minimal example
Export a default function that takes a Request and returns a Response, and add `export const runtime = 'edge'` to opt in. The Web Streams API is your friend for streaming — return a Response wrapping a ReadableStream and clients start receiving bytes before the handler finishes. Add cache headers (Cache-Control, s-maxage, stale-while-revalidate) to let the edge cache the response next to the user; this is how a dynamic route can behave almost like a static asset for repeat visitors.
Common pitfalls
- Assuming process.env is populated at build time — Edge reads env at request time; check for undefined.
- Reaching for Buffer or Node streams — use Uint8Array and Web Streams instead.
- Making a Postgres TCP connection — most Postgres drivers use raw TCP, which Edge cannot open; use HTTP-based drivers (Neon, Supabase, PlanetScale).
- Blocking on a slow upstream — the 30s ceiling is real; add AbortController with a timeout.
- Logging huge objects — Edge logs are truncated aggressively; log IDs and pull details from a store.
How Edge affects your architecture
Once you commit to Edge for the hot path, you tend to push more work into it: session decoding, feature flag lookups, personalization, geo routing, even lightweight rendering. That is fine, but be careful about state. Because isolates are short-lived and horizontally distributed, in-memory caches survive only a few requests before being evicted. For anything shared across users or regions, put the cache in an external store (KV, Redis, Data Cache) rather than the isolate's heap. The winning shape is usually: Edge for read, Serverless or a real service for expensive writes.