Open-Source Wikis

/

Next.js

/

Systems

/

Middleware and edge

vercel/next.js

Middleware and edge

middleware.ts runs in the Edge runtime — a Web Standards subset built on @edge-runtime/vm. The same edge runtime executes Edge Route Handlers (export const runtime = 'edge') and the streaming code paths that Vercel's edge network deploys.

Source: packages/next/src/server/web/.

What is the edge runtime?

The edge runtime is a sandboxed JavaScript environment that exposes only Web Standards: fetch, Request, Response, Headers, URL, URLPattern, crypto.subtle, ReadableStream, TransformStream, etc. Not available: node:* modules, Buffer, process (mostly), __dirname, __filename, async fs.

The runtime is implemented by @edge-runtime/vm (a Node vm context with the Web Standards globals injected). Vercel's edge network runs the same shape natively against V8 isolates.

Source: packages/next/src/server/web/.

Layout

packages/next/src/server/web/
├── adapter.ts                    # The shim user middleware is wrapped in (18.5 KB)
├── edge-route-module-wrapper.ts  # Edge route handler wrapper (6 KB)
├── exports/                      # Public surface of next/server (NextRequest, NextResponse, etc.)
├── globals.ts                    # Globals injected into the sandbox
├── http.ts                       # Request/Response shims
├── internal-edge-wait-until.ts   # waitUntil() implementation
├── next-url.ts                   # NextURL — extends URL with locale + base path
├── sandbox/                      # The @edge-runtime/vm wrapper
├── spec-extension/               # Web standards extensions (cookies, etc.)
├── types.ts
├── utils.ts                      # Edge-runtime helpers
└── web-on-close.ts               # Connection-close detection

How middleware runs

graph LR
    Req[Request] --> Router[router-server]
    Router --> Match{matches<br/>middleware<br/>matcher?}
    Match -- yes --> Adapter[adapter.ts wrapper]
    Adapter --> Sandbox[sandbox/<br/>@edge-runtime/vm]
    Sandbox --> User[User middleware<br/>middleware.ts]
    User --> Result{returns<br/>NextResponse?}
    Result -- yes --> ApplyHeaders[apply rewrite/redirect/headers]
    Result -- no --> Continue[continue routing]
    ApplyHeaders --> Router
    Continue --> Router

    Match -- no --> Continue

packages/next/src/server/web/adapter.ts is the middleware shim:

  1. Wraps the user's middleware.ts export.
  2. Extracts the matcher config and produces the manifest entry.
  3. At request time, runs in the edge sandbox, passes the request to the user function.
  4. Translates the returned NextResponse into a rewrite, redirect, header mutation, or next() (continue).

Edge route handlers

packages/next/src/server/web/edge-route-module-wrapper.ts (6 KB) is the analogous wrapper for App Router route handlers that opt into the edge runtime. It hosts the same execution shape as middleware.

NextURL and NextResponse

packages/next/src/server/web/next-url.ts extends the standard URL with locale and basePath awareness. next/server exports NextRequest (extending Request) and NextResponse (extending Response) from packages/next/src/server/web/exports/.

NextResponse adds:

  • NextResponse.next() — continue routing
  • NextResponse.redirect(url, status) — issue redirect
  • NextResponse.rewrite(url) — internal rewrite
  • .cookies — cookie helpers (read/set)

Globals

packages/next/src/server/web/globals.ts is what the sandbox injects as the global object. Everything not listed there is unavailable inside edge code.

waitUntil and after

Edge-runtime equivalents of unstable_after:

  • packages/next/src/server/web/internal-edge-wait-until.ts — the waitUntil primitive that asks the edge platform to keep the connection alive until a promise settles.
  • packages/next/src/server/after/ — the unstable_after API. On Node, queues into the after-task async storage; on edge, hands off to waitUntil.

Connection close detection

packages/next/src/server/web/web-on-close.ts exposes a primitive that fires when the client disconnects. Used by long-running streamed responses to abort upstream work when the user navigates away.

DCE rules for edge code

Edge bundles must not pull in Node-only modules. The framework applies several rules to enforce this:

  1. define-env.ts forces feature flags that gate Node-only imports to false for the edge layer. webpack DCE then removes the dead branches.
  2. require() calls for Node modules must be guarded with a compile-time if/else branch. Don't use early-return / throw patterns — those leave the require in the bundle.
  3. module.compiled.js selects between Node and Edge implementations of vendored libraries (react-dom/server.node vs react-dom/server.edge).

The agent skills dce-edge and runtime-debug cover this end-to-end.

Bundling

The webpack config produces a separate edge bundle alongside the server and client bundles. Webpack's externals list for the server bundle is mostly unfilled for the edge bundle — the edge can't externalize Node, so everything must be bundled in.

Edge bundles are written under .next/server/middleware-* and .next/server/edge-runtime-webpack-*.

Manifest

middleware-manifest.json (written by the build) lists each middleware entry, its compiled file, its matcher pattern, and the runtime config. The router-server reads this at startup to set up dispatch.

Integration points

  • Middleware bundles run inside @edge-runtime/vm in dev and prod.
  • Vercel deploys the same bundles to its edge network natively.
  • Edge route handlers share the same runtime as middleware.
  • The router server reads middleware-manifest.json to dispatch.

Entry points for modification

  • To change the middleware shim: packages/next/src/server/web/adapter.ts.
  • To change NextResponse behavior: packages/next/src/server/web/spec-extension/.
  • To change globals: packages/next/src/server/web/globals.ts.
  • To change DCE plumbing: packages/next/src/build/define-env.ts (consult the dce-edge agent skill).

Key source files

File Purpose
packages/next/src/server/web/adapter.ts Middleware shim
packages/next/src/server/web/edge-route-module-wrapper.ts Edge route handler shim
packages/next/src/server/web/sandbox/ @edge-runtime/vm wrapper
packages/next/src/server/web/globals.ts Edge sandbox globals
packages/next/src/server/web/next-url.ts NextURL
packages/next/src/server/web/spec-extension/ Web Standards extensions
packages/next/src/server/web/internal-edge-wait-until.ts waitUntil plumbing
packages/next/src/server/after/ unstable_after Node + Edge implementation
packages/next/src/build/define-env.ts Build-time DCE flag injection

Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.

Middleware and edge – Next.js wiki | Factory