Tradeoffs

Edge Functions vs API Servers: Which to Deploy

edge functionsAPI serversdeploymentCloudflare WorkersNode.jstradeoffsbackend

Choosing between edge functions and a traditional API server is one of those decisions that looks simple on the surface — until you're three months in and hitting cold-start timeouts or discovering your ORM doesn't run outside Node. This post breaks down the real tradeoffs between edge functions and traditional API servers, gives you a concrete decision checklist, and tells you exactly which signals should push you toward one or the other.

What each model actually is

Traditional API servers run your application code on a fixed set of machines — a VPS, a container cluster, or a managed compute service like Railway, Render, or Fly.io. Requests travel from the user to a single region (or a small set of regions you explicitly configure), hit your process, and return a response. Your code runs in a full Node.js, Python, or Go environment with access to the filesystem, long-lived connections, and the full package ecosystem.

Edge functions run your code at dozens or hundreds of points of presence distributed globally — Cloudflare Workers, Vercel Edge Functions, Deno Deploy, and similar platforms. A user in Seoul hits a node in Seoul; a user in São Paulo hits one in São Paulo. The runtime is stripped-down: no filesystem, limited CPU time per invocation, a restricted subset of Web APIs, and — critically — no persistent in-process state between requests.

The pitch for edge is obvious: lower latency for geographically distributed users. The catch is equally obvious once you've tried it: most real applications have dependencies that simply don't fit the edge runtime model.

The four axes that actually matter

1. Latency profile of your users

Edge wins when your users are genuinely global and your responses can be assembled at the edge — think personalized HTTP headers, A/B flags, lightweight auth token validation, or content that can be pulled from a globally replicated KV store. The latency improvement is real and measurable in those cases.

It is largely irrelevant if your application makes a round-trip to a single-region Postgres database on every request. You've shavedundefinedms off the network hop to the edge node, then added it back (plus more) on the database call. The bottleneck moves, it doesn't disappear.

2. Runtime compatibility

Traditional API servers give you the full runtime. Edge runtimes give you a subset — typically the WinterCG-compatible Web APIs: fetch, Request, Response, crypto, URL, and a handful of others. What you lose:

  • Native Node.js modules (fs, net, child_process, http)
  • Most ORMs in their default configuration (Prisma requires a special edge client; Drizzle works with some adapters; raw pg does not run at all)
  • Any package that calls native binaries or C extensions

If your API layer is thin — JWT verification, feature flag lookups, simple transformations — edge is fine. If it orchestrates database queries, sends email via a Node SDK, or calls a library with native bindings, you're fighting the runtime.

3. Cold starts and execution duration limits

Traditional servers stay warm. A long-running Node process handles request after request with no spin-up cost, which matters for anything with a non-trivial initialization path — loading a large ML model, establishing a connection pool, parsing a big config file.

Edge functions are ephemeral. Cloudflare Workers have aundefinedms CPU time limit on the free tier (50 ms on paid) and aundefinedMB memory cap. Vercel Edge Functions cap atundefinedseconds wall-clock time butundefinedseconds CPU. These limits are fine for lightweight middleware; they are hard blockers for anything computationally intensive.

4. Operational complexity

A single-region API server on Fly.io or Railway is genuinely simple to operate. One deployment target, one set of environment variables, logs in one place, a straightforward rollback story. Edge deployments spread your code across a global network, which means debugging a request that behaved differently in Tokyo than in Frankfurt requires distributed tracing tooling you may not have set up.

Decision checklist

Use this before committing to either path:

  • [ ] Are your users geographically distributed? If >70% are in one region, edge latency gains are marginal.
  • [ ] Does every request touch a single-region database? If yes, edge doesn't eliminate your slowest hop.
  • [ ] Do your dependencies run in a WinterCG runtime? Check each package. One incompatible library blocks the whole approach.
  • [ ] Is your per-request CPU work under ~5 ms? If not, edge CPU limits will bite you.
  • [ ] Do you need persistent in-process state (connection pools, caches, singletons)? Traditional server wins here.
  • [ ] Is your team already operating a distributed tracing stack? If not, edge debugging is harder than it looks.
  • [ ] Are you building middleware, auth, or routing logic only? Edge is a natural fit for this layer specifically.

If you checked more boxes in the "traditional server" column, deploy a single-region API server and revisit edge for specific middleware layers later. If you checked mostly "edge-friendly" boxes, edge functions will genuinely serve you well.

Where hybrid architectures make sense

The sharpest pattern in production right now isn't "edge OR server" — it's edge for the outer layer and a traditional server (or serverless function in a fixed region) for the data layer. Concretely:

  • Edge function validates the JWT and checks a KV-backed rate limit (~1 ms)
  • Edge function forwards authenticated requests to a regional API server that owns the database connection pool
  • The regional server handles business logic, queries Postgres, and returns the response

This gives you fast auth and rate-limiting globally without forcing your ORM into a hostile runtime. The tradeoff is added architectural complexity and one extra network hop for the forwarded request — usually 5–15 ms between edge PoP and regional origin, which is acceptable for most apps.

Frameworks like Next.js (App Router with middleware), Nuxt with Nitro, and Hono are all designed with this hybrid model in mind. If you're evaluating frameworks that support this pattern, the /compare tool on CraftMyStack lets you put them side by side across deployment model, runtime support, and ecosystem maturity.

Key takeaways

  • Edge functions reduce latency for global users only when the response can be assembled without a round-trip to a single-region database.
  • Runtime compatibility is the most common blocker — audit your dependencies before committing to an edge deployment.
  • CPU time limits (5–50 ms depending on platform) make edge unsuitable for computationally intensive request handling.
  • Traditional API servers are simpler to operate, debug, and scale vertically; they remain the right default for most CRUD-heavy applications.
  • Hybrid architectures — edge middleware forwarding to a regional server — capture the latency benefits without the runtime constraints.
  • The decision should be driven by your actual user geography, your dependency graph, and your team's operational maturity, not by what's currently fashionable.

If you're still uncertain which deployment model fits your specific stack — or you're picking a framework and want to understand how each handles edge compatibility — use the /recommend flow on CraftMyStack to get a grounded recommendation based on your actual constraints. And if you're building out the rest of your toolchain at the same time, MatchMyTool can help you find the supporting services (monitoring, logging, CI/CD) that slot cleanly into whichever deployment model you land on.

Never miss a prompt breakthrough

Join 500+ builders getting focused email updates whenever we publish. Unsubscribe anytime — or follow the RSS feed.

Prefer a reader? RSS feed