# Data flow — vercel/next.js

> Canonical page: https://codevetter.com/unpack/nextjs/data-flow

# Data flow — vercel/next.js

Scanned commit 2861a364fcbe75fe96b0b9127f9b335edfcf288c (2026-09-22). Generated by codevetter codevetter 1.12.0 on 2026-09-22.

A request enters at the Node `http.Server` in `start-server.ts`, is immediately sanitised by `filterInternalHeaders()` (which deletes a fixed `INTERNAL_HEADERS` list so clients cannot forge `x-middleware-rewrite`, `x-matched-path`, etc.), then flows through the router-server's `resolveRoutes` pipeline (headers → redirects → middleware → beforeFiles → filesystem → afterFiles → dynamic routes → fallback), and only then crosses into the render server. Inside `BaseServer.handleRequest` the request is wrapped in an OTEL span and per-request metadata is attached via `addRequestMeta`, after which render state is carried in AsyncLocalStorage (`workAsyncStorage`, `workUnitAsyncStorage`) rather than threaded through arguments. Output is a `RenderResult` piped to the socket, with cacheable results passing through `ResponseCache` → `IncrementalCache` → a `CacheHandler`. Build-time data flows the other direction: source files → route discovery → bundler (Turbopack over napi, or webpack) → `.next/*-manifest.json` → read back by `setupFsCheck` at server boot.
- The first thing `requestHandlerImpl` does with a live request is `addRequestMeta(req, 'relativeProjectDir', ...)` and then `filterInternalHeaders(req.headers)` — unless `NEXT_PRIVATE_TEST_HEADERS` is set, which is the documented test-only escape hatch. — [packages/next/src/server/lib/router-server.ts#L307-L314](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/next/src/server/lib/router-server.ts#L307-L314) [packages/next/src/server/lib/server-ipc/utils.ts#L38-L62](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/next/src/server/lib/server-ipc/utils.ts#L38-L62)
- `INTERNAL_HEADERS` is an explicit allow-nothing list of 11 names (`x-middleware-rewrite`, `x-middleware-redirect`, `x-middleware-set-cookie`, `x-middleware-skip`, `x-middleware-override-headers`, `x-middleware-next`, `x-now-route-matches`, `x-matched-path`, `x-nextjs-data`, `x-next-resume-state-length`, `next-resume`); any new internal header must be added here or it is forgeable from outside. — [packages/next/src/server/lib/server-ipc/utils.ts#L38-L52](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/next/src/server/lib/server-ipc/utils.ts#L38-L52) [AGENTS.md](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/AGENTS.md)
- Routing is a declarative ordered list built once in `resolve-routes.ts`: `middleware_next_data`, `middleware`, `rewrites.beforeFiles`, a second middleware check, `rewrites.afterFiles`, then `rewrites.fallback` — with all rewrite phases skipped entirely when `opts.minimalMode` is set (the deployed/serverless shape). — [packages/next/src/server/lib/router-utils/resolve-routes.ts#L86-L118](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/next/src/server/lib/router-utils/resolve-routes.ts#L86-L118)
- Middleware runs out-of-process-shaped: `resolveRoutes` sets `middlewareInvoke: true` request meta, invokes the render server, and then translates the returned `Response` headers — hoisting `x-middleware-override-headers` into real request headers by reading each `x-middleware-request-<key>` and deleting it, and synthesising `x-middleware-refresh` when neither rewrite, next, nor location was set. — [packages/next/src/server/lib/router-utils/resolve-routes.ts#L566-L710](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/next/src/server/lib/router-utils/resolve-routes.ts#L566-L710)
- `setupFsCheck()` is the build→runtime data handoff: it reads `routes-manifest.json`, the app/pages routes manifests, and the `public/` + `.next/static/` directory listings into `Set`s (`publicFolderItems`, `nextStaticFolderItems`, `appFiles`, `pageFiles`) plus a sorted `dynamicRoutes` array and a `middlewareMatcher`, then exposes a single LRU-backed `getItem(pathname)`. — [packages/next/src/server/lib/router-utils/filesystem.ts#L192-L340](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/next/src/server/lib/router-utils/filesystem.ts#L192-L340) [packages/next/src/server/lib/router-utils/filesystem.ts#L489-L700](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/next/src/server/lib/router-utils/filesystem.ts#L489-L700)
- Per-request state is passed via `addRequestMeta`/`getRequestMeta` keys (`invokePath`, `invokeQuery`, `middlewareInvoke`, `locale`, `isRSCRequest`, `relativeProjectDir`) rather than function parameters, which is what lets the router server and render server share a request object across the lazy-init boundary. — [packages/next/src/server/lib/router-server.ts#L460-L480](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/next/src/server/lib/router-server.ts#L460-L480) [packages/next/src/server/request-meta.ts](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/next/src/server/request-meta.ts) [packages/next/src/server/base-server.ts#L899-L905](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/next/src/server/base-server.ts#L899-L905)
- Render-time state lives in AsyncLocalStorage: `app-render.tsx` repeatedly does `workUnitAsyncStorage.run(...)` to produce the RSC payload and the Flight stream, and `workAsyncStorage` carries the work store; there is no explicit context object threaded through the React tree. — [packages/next/src/server/app-render/app-render.tsx#L19-L25](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/next/src/server/app-render/app-render.tsx#L19-L25) [packages/next/src/server/app-render/app-render.tsx#L975-L1025](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/next/src/server/app-render/app-render.tsx#L975-L1025)
- Cache Components prerendering is a two-pass flow: a prospective prerender fills a `PrerenderResumeDataCache` created by `createPrerenderResumeDataCache()`, that cache is attached to `requestStore.resumeDataCache`, and a second 'final'/runtime prerender reuses the filled cache — the comments state caches must be shared between the prospective and final prerender. — [packages/next/src/server/app-render/app-render.tsx#L1111-L1145](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/next/src/server/app-render/app-render.tsx#L1111-L1145) [packages/next/src/server/app-render/app-render.tsx#L1597-L1730](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/next/src/server/app-render/app-render.tsx#L1597-L1730)
- Cacheable responses flow value-as-stream: a `CacheEntry.value` is a `ReadableStream<Uint8Array>` with a documented contract that handlers must return a new stream on every `get` and must not retain the stream after `set` resolves, because a retained stream pins the originating request's async context. — [packages/next/src/server/lib/cache-handlers/types.ts#L6-L30](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/next/src/server/lib/cache-handlers/types.ts#L6-L30)
- Tag-based revalidation is a side-channel: `FileSystemCache` mutates a module-scoped `tagsManifest` map with `{ expired: now }` entries and reads it back through `areTagsExpired`, so tag state is per-process in the filesystem handler. — [packages/next/src/server/lib/incremental-cache/file-system-cache.ts#L24-L100](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/next/src/server/lib/incremental-cache/file-system-cache.ts#L24-L100)
- Build-time config is projected into user bundles as `process.env.__NEXT_*` string literals by `getDefineEnv()` — 40+ keys including `__NEXT_BUNDLER`, `__NEXT_DEV_SERVER`, `__NEXT_CACHE_COMPONENTS`, `__NEXT_USE_CACHE`, `__NEXT_IMAGE_OPTS`, and the client router bloom filters `__NEXT_CLIENT_ROUTER_S_FILTER`/`_D_FILTER`. — [packages/next/src/build/define-env.ts#L82-L285](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/next/src/build/define-env.ts#L82-L285)
- `process.env.__NEXT_USE_NODE_STREAMS` is set to `false` for the edge server and `true` otherwise, which is the fork point for which stream implementation the render path compiles against. — [packages/next/src/build/define-env.ts#L192](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/next/src/build/define-env.ts#L192)
- Outbound network egress from the framework is narrow and bounded: the image optimizer `fetch`es upstream images with `AbortSignal.timeout(7_000)` and `redirect: 'manual'`, following at most 3 redirects manually. — [packages/next/src/server/image-optimizer.ts#L517-L575](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/next/src/server/image-optimizer.ts#L517-L575)
- Build output data leaves the repo through the adapter boundary: `handleBuildComplete()` resolves `adapterPath` with `require.resolve` + `pathToFileURL` and, for `output: 'export'`, enumerates `configOutDir` with `recursiveReadDir` to hand the adapter a static-file list. — [packages/next/src/build/adapter/build-complete.ts#L595-L685](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/next/src/build/adapter/build-complete.ts#L595-L685)

Every claim links to its cited files at the pinned commit. Citations identify source evidence; they do not establish runtime correctness. Independent CodeVetter scan — not reviewed by upstream maintainers.

## Public product links

- [CodeVetter](https://codevetter.com/)
- [Download](https://codevetter.com/download)
- [Documentation](https://codevetter.com/docs/)
- [Source](https://github.com/Codevetter/codevetter)
