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 detectionHow 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 --> Continuepackages/next/src/server/web/adapter.ts is the middleware shim:
- Wraps the user's
middleware.tsexport. - Extracts the
matcherconfig and produces the manifest entry. - At request time, runs in the edge sandbox, passes the request to the user function.
- Translates the returned
NextResponseinto a rewrite, redirect, header mutation, ornext()(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 routingNextResponse.redirect(url, status)— issue redirectNextResponse.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— thewaitUntilprimitive that asks the edge platform to keep the connection alive until a promise settles.packages/next/src/server/after/— theunstable_afterAPI. On Node, queues into the after-task async storage; on edge, hands off towaitUntil.
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:
define-env.tsforces feature flags that gate Node-only imports tofalsefor the edge layer. webpack DCE then removes the dead branches.require()calls for Node modules must be guarded with a compile-timeif/elsebranch. Don't use early-return / throw patterns — those leave the require in the bundle.module.compiled.jsselects between Node and Edge implementations of vendored libraries (react-dom/server.nodevsreact-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/vmin 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.jsonto dispatch.
Entry points for modification
- To change the middleware shim:
packages/next/src/server/web/adapter.ts. - To change
NextResponsebehavior: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 thedce-edgeagent 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.