Edge Runtime vs Node Runtime in Next.js: When to Use What
AI generated
</>
{ }
React · Next.js · Edge Runtime · Node.js
Edge Runtime vs Node Runtime in Next.js
Cold starts, Node APIs, and the right choice per route

The edge runtime promises minimal cold start times and global distribution, but gives up central Node APIs in exchange. The Node runtime offers full compatibility, but costs latency for globally distributed users. Deciding deliberately per route gets the best out of both models.

18 min read Edge · Node · Cold Start · Database Next.js 14/15 · Vercel

1. Edge runtime and Node runtime: the basics

Next.js supports two different execution environments for server code: the edge runtime and the Node runtime. The Node runtime is the classic, complete Node.js environment, with access to the entire Node ecosystem, all npm packages, and native modules. The edge runtime, on the other hand, is a deliberately reduced JavaScript environment based on web standard APIs, designed to run at many geographically distributed locations at once with minimal startup time.

The fundamental difference between the edge runtime and the Node runtime is not the programming language, both execute JavaScript or TypeScript, but the underlying execution model. The Node runtime typically runs in a few, but powerful data centers, while the edge runtime relies on V8 isolates, the same isolation mechanism Cloudflare Workers also use, to keep code ready at hundreds of locations worldwide, physically closer to the user.

This distinction also matters because it is not limited to Next.js: other frameworks like Remix or SvelteKit offer similar concepts under different names, usually also with a split between a lean, web-API-based execution environment and a full Node environment. Once you understand this pattern in Next.js, you can directly transfer that knowledge to related decisions in other modern meta-frameworks.

For Next.js developers this means: route handlers, middleware, and Server Components can each independently configure whether they run in the edge runtime or the Node runtime. This choice is not a global project setting, it is a deliberate decision per route that should be based on the actual requirements of the respective functionality, not on a blanket preference for the supposedly more modern option.

Historically, the distinction between the edge runtime and the Node runtime is a direct response to the limits of classic serverless platforms, where every function was started in its own, complete container. With the edge runtime, Next.js adopts a model originally developed for content delivery networks and makes it usable for server-side application logic, without developers having to directly address a separate platform like Cloudflare Workers.

2. Technical limits of the edge runtime

The edge runtime deliberately gives up anything that would threaten its low startup time. No filesystem access, no native TCP sockets, no Node-specific bindings to C++ addons. Instead, the same APIs that exist in the browser are available: fetch, Request, Response, URL, TextEncoder, and the Web Crypto API for cryptographic operations.

This web-API foundation has a pleasant side effect for teams already experienced with browser JavaScript: code that runs in the edge runtime can, in many cases, be tested almost unchanged on the client side too, because the same interfaces are used. This overlap significantly simplifies writing unit tests, without needing to simulate a full Node or edge runtime environment.

This limitation of the edge runtime is not an oversight, it is a deliberate design decision: every additional capability would either increase isolate startup time or weaken the sandbox's security guarantees. Libraries that rely heavily on Node-specific APIs, for example classic ORMs with direct TCP database connections or image processing libraries with native bindings, simply do not work in the edge runtime and must either be replaced or moved to the Node runtime.

The isolation boundaries of the edge runtime are best compared to a strict sandbox that deliberately does less than a full operating system environment, but in exchange safely separates many parallel tenants from each other. The same property that excludes Node modules allows platform operators to run thousands of independent customer functions on shared hardware, without one isolate being able to access another's resources.

In practice, this means development teams should briefly check every new dependency before using it in an edge runtime route, verifying that it exclusively uses web standard APIs. Many modern libraries now explicitly flag their edge compatibility in package.json via the exports field with a separate edge-light condition, which considerably simplifies this check.


// app/api/edge-example/route.ts — explicitly opting into the Edge Runtime
export const runtime = 'edge';

export async function GET(request: Request) {
  // Only Web APIs available here — no fs, no native TCP
  const url = new URL(request.url);
  const response = await fetch('https://api.example.com/data');
  const data = await response.json();

  return new Response(JSON.stringify(data), {
    headers: { 'Content-Type': 'application/json' },
  });
}

3. When the edge runtime wins on performance

The clearest advantage of the edge runtime shows up with globally distributed user groups. Because code runs at dozens or hundreds of locations worldwide, physical network latency to a central data center disappears. A user in Singapore calling a Node runtime function that only runs in Frankfurt pays a noticeable latency penalty, while the same logic in the edge runtime can be served from a location near Singapore.

A second performance advantage concerns cold start time itself: V8 isolates start in a few milliseconds, while a full Node process instance, especially with many loaded dependencies, noticeably takes longer to boot. For functions that are rarely called and therefore frequently go through a cold start, say middleware on low-traffic routes, this difference accounts for most of the perceived response time.

A third, often underestimated advantage concerns scaling under load spikes. Because V8 isolates need considerably less memory and startup time than full Node processes, a platform can provision additional edge runtime instances much faster during a sudden traffic surge, while the Node runtime scales up noticeably more slowly under comparable load.

4. Node APIs missing from the edge runtime

Concretely, the edge runtime is missing, among others, the fs module for filesystem access, child_process for spawning external processes, native net and tls sockets for direct TCP connections, and many Node-specific Buffer operations that differ from the web standard ArrayBuffer API. process.env also behaves more restrictively in the edge runtime: only variables explicitly embedded at build time are available, dynamically loading from a .env file at runtime does not work.

Timer functions also behave differently than usual in the edge runtime: long-running setInterval calls meant to outlive a single request do not work reliably, because an isolate can be terminated at any time after the response completes. Background processes that need to run continuously therefore fundamentally do not belong in the edge runtime, regardless of whether they use Node APIs or not.

Practically, this means for many popular libraries: a classic PDF generator relying on native bindings, an image processing package like sharp, or a database driver with a direct TCP handshake like the classic pg client for PostgreSQL, do not work in the edge runtime. For all these cases, either the Node runtime remains the right choice, or there is an edge-compatible alternative that uses HTTP instead of TCP for communication.

An often overlooked side effect of these limits concerns logging and monitoring libraries that, in classic Node applications, hook deeply into process events or native performance hooks. Such libraries must offer specially adapted variants for the edge runtime that rely exclusively on web APIs, otherwise the build itself fails, long before an actual request ever reaches the affected code.

5. Configuring the runtime per route

Next.js allows explicitly setting the runtime per route handler or Server Component, via the exported constant export const runtime = 'edge' or export const runtime = 'nodejs'. If this is missing, route handlers default to the Node runtime, while middleware always starts in the edge runtime unless explicitly configured otherwise. These different defaults are a common source of confusion for teams new to the App Router.

For a mixed project, it is common to leave most database-heavy route handlers on the Node runtime, while individual, latency-critical and stateless endpoints, say a feature flag lookup endpoint or a simple health check route, are explicitly switched to the edge runtime. This selective migration reduces risk, because only the routes that actually benefit get switched, instead of converting the entire project at once.

An additional practical tip: team-internal conventions, say a linter rule that warns on a missing runtime declaration in route handlers, make the decision immediately visible in the code for every contributor, instead of hiding it implicitly in whatever the default happens to be. This reduces accidental regressions when a new developer copies an existing route and unintentionally inherits the wrong runtime.


// app/api/health/route.ts — cheap, stateless endpoint benefits from Edge Runtime
export const runtime = 'edge';

export function GET() {
  return Response.json({ status: 'ok', timestamp: Date.now() });
}

// app/api/orders/route.ts — heavy DB access stays on the Node Runtime
export const runtime = 'nodejs';

import { db } from '@/lib/db';

export async function GET() {
  const orders = await db.order.findMany({ take: 50 });
  return Response.json(orders);
}

6. Database access: edge-compatible clients

Database access is the most common reason teams struggle with the edge runtime. Classic drivers open a persistent TCP connection, which is technically not possible in the edge runtime. The solution is HTTP-based database clients, which several providers have now developed specifically for this purpose: Neon and PlanetScale offer HTTP endpoints for PostgreSQL and MySQL respectively, which work without a persistent connection and function fine in the edge runtime.

Important to understand: these HTTP-based clients have different performance characteristics than classic connection pools. Every query effectively establishes a new connection, which is unproblematic for individual, rare queries but can lead to accumulated latency with many sequential queries within a single request. For applications with complex, multi-step database queries, the Node runtime with a classic connection pool often remains the better choice, even if the edge runtime would technically be available.

An emerging middle ground is connection-pooling services that themselves run as an HTTP facade in front of a classic database server, for example PgBouncer combined with an HTTP gateway. This architecture allows keeping the benefits of a real connection pool while the actual edge runtime route still only communicates over HTTP, without having to manage a TCP connection itself.

7. Cold start behavior and global distribution

Cold start refers to the time an execution environment needs to move from an inactive state to a runnable state. With the Node runtime in classic serverless environments, a cold start, depending on the number of loaded dependencies and the size of the deployment package, can take several hundred milliseconds up to a few seconds. The edge runtime reduces this time through V8 isolates typically to a low single-digit or low double-digit millisecond range.

Global distribution further amplifies this effect: because edge runtime instances can be kept ready at many locations at once, the probability of a user ever experiencing a cold start drops, since a warm isolate likely already exists nearby. With the Node runtime and its few central locations, the probability of a cold start for users in distant regions is structurally higher, because fewer parallel instances are kept ready.

Teams wanting to measure this effect concretely benefit from a synthetic test from several geographic regions against the same route, once configured with the edge runtime and once with the Node runtime. The difference usually shows up most clearly for users located far from the nearest Node runtime data center, while users close to such a location barely notice a difference.

8. Migration strategy between the runtimes

A migration from the Node runtime to the edge runtime should never happen as a big-bang switch. The proven approach starts with an inventory: which route handlers actually use Node-specific APIs, and which are already effectively stateless and web-API compatible? Middleware is usually the easiest starting point, because it already runs in the edge runtime and typically contains little Node-specific logic.

After middleware come individual, clearly scoped route handlers, starting with those without database access. Database-heavy routes are migrated only after an edge-compatible database client has been established and tested. A common mistake during this migration is enabling the edge runtime on a trial basis without systematically checking which transitive dependencies in a package hide Node APIs, which then only surfaces at build time or, worse, only at runtime.

A proven approach is to secure every migration behind its own feature branch with automated end-to-end tests that specifically check the routes whose runtime is being switched. That way a regression, say a failing database query in the edge runtime, gets caught during testing instead of being noticed only through user complaints in production.

An additional safety net is making the runtime configuration controllable through an environment variable instead of a hardcoded value, so that in an emergency a quick rollback from the edge runtime back to the Node runtime is possible without a new deployment cycle. This safety net has proven decisive across several Next.js migrations to avoid production outages during the switchover.

9. Edge runtime vs Node runtime compared

The following table summarizes the most important decision criteria between the edge runtime and the Node runtime.

Criterion Edge Runtime Node Runtime Recommendation
Cold start time Very low Higher, depends on package size Edge for frequent cold starts
Node-specific APIs Not available Fully available Node for fs, TCP, native modules
Database with connection pool Only via HTTP client Natively supported Node for complex queries
Global user base Physically close to the user Central locations Edge for latency-critical, stateless logic
npm package compatibility Restricted Full Node for complex dependencies

The table shows: there is no universally superior runtime. The edge runtime wins for latency-critical, stateless logic with a global user base, the Node runtime remains the right choice for complex database access and dependencies with Node-specific APIs.

As a rule of thumb for teams without existing Next.js experience with both runtimes: when in doubt, start with the Node runtime first, since it offers the lowest probability of unexpected compatibility issues, and deliberately migrate individual, clearly latency-critical routes to the edge runtime later, once a real need and a measured latency benefit exist.

Mironsoft

Next.js architecture and runtime decisions

The right runtime for every route, not just a blanket choice?

We analyze existing Next.js route handlers and middleware, identify edge candidates, and migrate step by step, with edge-compatible database clients where needed.

Runtime audit

Identifying Node dependencies per route and marking edge candidates

Step-by-step migration

Low-risk, route-by-route switchover with rollback capability

Database integration

Edge-compatible HTTP clients for Neon, PlanetScale, and similar providers

10. Summary

Choosing between the edge runtime and the Node runtime in Next.js is not a question of old versus new, it is a deliberate decision per route, based on actual requirements. The edge runtime wins on cold start time and global distribution, but gives up central Node APIs like filesystem access, native TCP sockets, and many npm packages with native bindings. The Node runtime remains the right choice for database-heavy logic with connection pools and complex dependencies.

A successful migration proceeds step by step, starting with middleware and stateless routes, supported by edge-compatible database clients for the remaining cases. Anyone making this decision as a blanket rule instead of a differentiated one risks either unnecessary latency from too much Node runtime, or build errors and performance problems from a rushed edge runtime migration without a clean inventory.

In the long run, it pays off to treat the runtime decision as a living part of the architecture rather than a one-time determination. A growing Next.js codebase changes its requirements over time, new dependencies get added, existing data access patterns change, and a route that runs fine in the edge runtime today can suddenly acquire Node-specific requirements through a later change. Regular review prevents this drift from going unnoticed.

Edge Runtime vs Node Runtime: The Essentials at a Glance

Edge runtime strengths

Minimal cold start time, global distribution close to the user, ideal for stateless, latency-critical logic.

Node runtime strengths

Full Node API compatibility, connection pools, native npm packages without restriction.

Configure per route

export const runtime as a deliberate decision per route handler, not a global default.

Migrate step by step

Middleware first, then stateless routes, database access last with an edge-compatible client.

These principles apply regardless of whether an application runs on Vercel, a self-hosted Node environment, or another edge-capable platform.

11. FAQ: Edge Runtime vs Node Runtime

1Main difference?
Edge: minimal cold start, web APIs. Node: full ecosystem, central locations.
2Why no Prisma in edge?
Native TCP sockets are missing in the edge runtime. HTTP-based clients are the alternative.
3Configure runtime per route?
Set export const runtime = 'edge' or 'nodejs' directly in the file.
4Always edge for middleware?
By default yes, Node experimentally possible with longer cold starts.
5What is a V8 isolate?
Lightweight isolation mechanism without a full process start, the basis of edge speed.
6Existing Node library usable?
Only with pure web API usage, otherwise find an edge alternative or use the Node runtime.
7Worth it for regional users only?
Latency advantage smaller, cold start advantage remains relevant.
8Low-risk migration?
Middleware first, then stateless routes, database routes last, test individually.
9Transitive Node dependency?
Build error or runtime error, systematic check before migration reduces risk.
10HTTP DB slower than pool?
Comparable for single queries, accumulates latency with many sequential queries.