Edge vs Serverless: Choosing the Right Web Runtime
Understand edge vs serverless runtimes by latency profile, cold-start cost, and database compatibility — so you pick the right one for your app.

Your API route returns in 800ms. Users in Tokyo are waiting half a second longer than users in Virginia. The fix isn't a faster database query. It's where your function runs. The edge vs serverless decision is a geography and runtime-constraint problem, not just a cost-per-invocation spreadsheet.
Most teams reach for serverless functions by default because every cloud provider makes them easy to deploy. That's a reasonable starting point. But the edge runtime trades raw capability for physical proximity, and getting that trade-off wrong means paying the penalty on every single request.
We've spent the last two years routing production traffic through both runtimes at Laxaar, and the picture that emerges is more nuanced than "edge is always faster." The answer depends on what your function actually needs to do.
What you'll learn
- What the edge runtime actually is (and what it isn't)
- How serverless cold starts work and when they hurt
- Latency profiles: where each runtime wins
- Database clients: the biggest constraint on edge
- Edge vs serverless comparison table
- When to use edge runtime
- When serverless is the right call
- Frequently Asked Questions
What the edge runtime actually is
The edge runtime is a stripped-down, V8-based JavaScript environment deployed to dozens or hundreds of globally distributed PoPs (points of presence). Cloudflare Workers, Vercel Edge Functions, and Deno Deploy are the main players. Code runs in under 1ms of startup time because there's no container to spin up; the isolate is already warm.
The trade-off is real: the edge runtime deliberately excludes Node.js APIs. No fs, no child_process, no native binaries. HTTP-based APIs work fine. TCP connections (the kind your Postgres driver needs) don't, at least not natively. Memory limits are tight (128MB is common), and CPU time per request is bounded at a few milliseconds of wall-clock execution.
Think of the edge runtime as a global CDN layer that can run logic, not as a general-purpose compute environment.
How serverless cold starts work
A serverless function (AWS Lambda, Vercel Serverless, Google Cloud Functions) runs in a full container or microVM. The first invocation after a period of inactivity triggers a cold start: the runtime downloads your function bundle, initialises the process, and then runs your handler. For Node.js functions with heavy imports, this adds 200ms to 1,500ms on the first request.
Cold starts are the most misunderstood cost in serverless. They don't affect every request; provisioned concurrency and keep-alive patterns eliminate them for steady-state traffic. The problem surfaces during traffic spikes (when new instances spin up simultaneously) and low-traffic endpoints that go cold between calls.
A few things actually determine cold-start severity:
- Bundle size. Fewer imports, faster init. Tree-shaking matters.
- Runtime. Node.js is faster to initialise than JVM or .NET; Python sits in between.
- Region count. Deploying to one region limits geographic reach; deploying to many multiplies the cold-start surface.
Latency profiles where each runtime wins
Raw numbers from real production workloads tell the story better than benchmarks run on synthetic payloads.
For a lightweight auth check that validates a JWT and returns a user object, edge consistently returns in 10-30ms globally because the compute is near the user and the logic needs no external I/O. The same function in a single-region serverless deployment returns in 20ms for users in-region and 200-400ms for users on the other side of the planet.
For a function that queries a Postgres database with a complex JOIN, serverless in the same region as the database wins. An edge function making a round-trip to a database deployed in us-east-1 from a PoP in Singapore adds a 180ms network hop that a regional serverless deployment avoids entirely.
The practical rule: compute-heavy or I/O-to-a-single-region database means serverless; logic-only or globally distributed data means edge.
Database clients the biggest constraint on edge
This is the issue that catches most teams off guard. Traditional database drivers like pg, mysql2, and mongoose open persistent TCP connections. The edge runtime doesn't support raw TCP. Your Postgres connection string simply won't work.
Your options on edge today:
- HTTP-based database clients. Neon, PlanetScale (MySQL), and Turso all expose HTTP APIs that work inside edge runtimes. Neon's
@neondatabase/serverlessdriver uses WebSockets or HTTP and is the most widely used solution for Postgres on edge. - Edge-compatible ORMs. Drizzle supports Neon's HTTP driver. Prisma added edge support via Accelerate (a connection pooler proxy), though it adds latency.
- KV stores and Durable Objects. Cloudflare KV and Durable Objects are natively edge-aware. Redis over HTTP (Upstash) works well.
- Move the database call to a serverless function. The edge function handles auth and routing; it proxies data-fetching requests to a regional serverless function that holds the database connection.
Option 4 is underrated. A hybrid architecture where the edge layer handles the fast path and delegates to serverless for data access often outperforms a fully serverless approach because it shortens the user-to-first-byte time without fighting TCP constraints.
Edge vs serverless at a glance
| Dimension | Edge Runtime | Serverless Functions |
|---|---|---|
| Cold start | None (sub-1ms isolate) | 200ms–1,500ms (first invoke) |
| Startup latency | Excellent globally | Depends on deployment region |
| Node.js API support | Subset only (no fs, no TCP) | Full Node.js |
| Max execution time | 30s–50ms CPU (varies by provider) | Minutes (configurable) |
| Memory limit | 128MB typical | 128MB–10GB |
| Database (TCP) | No native support | Full support |
| Database (HTTP) | Yes (Neon, Upstash, Turso) | Yes |
| Ideal workload | Auth, routing, personalisation, A/B | Data queries, heavy computation, file I/O |
| Pricing model | Per-request + CPU time | Per GB-second |
We treat this table as a starting checklist, not a final verdict. A single application often needs both runtimes.
When to use edge runtime
Edge shines when your function is stateless, the logic is lightweight, and geographic proximity to users is the primary performance lever.
Auth middleware is the canonical case. Checking a JWT signature, validating a session cookie, or redirecting unauthenticated users doesn't require a database call. Running this at the edge in Next.js middleware means the redirect or header injection happens before a single byte of your app bundle is served, from a node close to the user.
// next.config.ts — route all /dashboard/* through edge middleware
export const config = {
matcher: ['/dashboard/:path*'],
};
// middleware.ts
import { NextRequest, NextResponse } from 'next/server';
import { verifyJWT } from '@/lib/auth';
export const runtime = 'edge';
export async function middleware(req: NextRequest) {
const token = req.cookies.get('session')?.value;
if (!token || !(await verifyJWT(token))) {
return NextResponse.redirect(new URL('/login', req.url));
}
return NextResponse.next();
}
Personalisation and A/B testing are also strong candidates. Reading a cookie to bucket a user into a variant and rewriting the URL, all without a round-trip to an origin server, is exactly what edge isolates were designed for.
Geo-routing is near-trivial at the edge. Cloudflare Workers expose request.cf.country; Vercel injects x-vercel-ip-country. Redirecting / to a localised domain costs a few lines of code and zero additional latency.
When serverless is the right call
Serverless functions are the right default for everything that needs the full Node.js environment or a database connection via TCP.
CRUD APIs against a relational database belong in serverless. A pg connection pool, database migrations, complex ORM queries with associations: none of these map cleanly to edge constraints. Deploy the function in the same region as your database and the round-trip is single-digit milliseconds.
File processing is a hard no for edge. Resizing an image with sharp, parsing a CSV, generating a PDF: all require native binaries or significant memory. Serverless handles these cleanly with generous memory limits and long-running execution.
Third-party SDK integrations also tend to be serverless territory. Stripe's Node.js SDK, AWS SDK v3, SendGrid: these are built for Node.js environments, test against Node.js, and may use dependencies that fail silently in an edge isolate.
The practical signal: if your function opens anything other than an HTTP connection, start with serverless. You can always push a thin edge layer in front later.
Our custom software development projects consistently use this split: a Next.js middleware layer at the edge for auth and routing, serverless functions for API routes touching the database. The architecture is simple to reason about and the performance gains are immediate.
Hybrid patterns worth knowing
The most effective production setups we've seen at Laxaar don't pick a side. They route by workload.
Edge for the fast path, serverless for data: the edge function handles auth in 15ms, attaches a x-user-id header, and passes the request to a regional serverless function that runs the query. Total latency for authenticated users in Tokyo drops from 400ms (single-region serverless) to 80ms.
ISR with edge revalidation: Next.js Incremental Static Regeneration caches pages at the CDN edge. A serverless function handles the revalidation webhook from your CMS. Users get cached HTML served from edge; content freshness is managed server-side. This pattern cuts origin traffic by 95% on content-heavy sites.
Edge for config, serverless for execution: feature flags evaluated at the edge route users to different serverless function versions. Zero-downtime rollouts without touching DNS.
If you're building a new web product and want help deciding which runtime fits each part of your stack, the Laxaar team offers architecture reviews as part of our web development and product engineering engagements.
Frequently Asked Questions
Can edge functions replace serverless entirely?
Not yet. Edge runtimes lack TCP support, native binaries, and long execution time, all of which matter for data-heavy APIs and file processing. They're an addition to serverless, not a replacement. Most production apps benefit from both: edge for the request routing layer, serverless for the data layer.
Do edge functions solve the cold-start problem?
Yes, for the startup portion. Edge isolates don't cold-start the way containers do. But this doesn't mean every request is instant. A slow origin database or a remote API call will still add latency. Edge eliminates the container-spin-up cost, not network I/O time.
Which databases work on the edge runtime today?
Neon (Postgres over HTTP/WebSocket), PlanetScale (MySQL over HTTP), Turso (libSQL over HTTP), and Upstash (Redis over HTTP) are production-ready. CockroachDB also offers an HTTP-compatible driver. For traditional Postgres with pg, use Prisma Accelerate or move the database query to a serverless function.
How does this choice affect Next.js specifically?
Next.js lets you set export const runtime = 'edge' per route segment or use it in middleware.ts. App Router Server Components run on serverless by default; you can opt individual route handlers into edge. The key rule: any route handler that imports a Node.js-only module will fail to deploy as an edge function, so check your dependency tree before switching.
Is edge runtime more expensive than serverless?
It depends on the provider and workload. Cloudflare Workers pricing is per-request with a very generous free tier. Vercel charges for edge function invocations separately from serverless. For high-traffic, compute-light workloads (auth checks, redirects) edge often costs less. For CPU-heavy tasks, the per-CPU-millisecond billing on edge can exceed serverless GB-second pricing.
What's the best way to test edge functions locally?
Vercel's vercel dev command emulates the edge runtime locally using the same V8 isolate. Wrangler (Cloudflare's CLI) does the same for Workers. Both catch runtime compatibility errors before deployment. We recommend running a runtime: edge compatibility check as part of your CI pipeline to surface import issues early.
Picking the right runtime upfront is far cheaper than refactoring a deployed API because you hit a TCP constraint six months in. If you're planning a new architecture or migrating an existing one, our team at Laxaar can help you map each route to the right runtime from the start. Get in touch or see how we've approached similar problems across our portfolio.
Working on something like this?
Get a fixed scope, timeline, and price within one business day — no obligation.


