Next.js Pages Router API handlers using `export const config = { api:
{ bodyParser: false } }` drain the raw bytes by iterating `req` directly
(`for await (const chunk of req)`), matching Node's IncomingMessage
contract. vinext's Workers-path req was a plain object with `body` set
to the underlying ReadableStream, so the idiom threw with 500 — breaking
webhook handlers and the `middleware-fetches-with-body` deploy suite.
Surface an `[Symbol.asyncIterator]` on the synthetic `PagesReqResRequest`
that proxies the underlying Web ReadableStream reader (with a `return()`
cleanup so `break` propagates a cancel). Leave `req.body` undefined when
`bodyParser: false`, matching Next.js (`parse-body.ts` only assigns
`req.body` when `bodyParser !== false`).
Ports the relevant assertion from
test/e2e/middleware-fetches-with-body/index.test.ts in the Next.js repo.
* fix(security): honor pages API bodyParser config (F-PROD-4)
Pages Router API routes export configuration like:
export const config = { api: { bodyParser: false } };
export const config = { api: { bodyParser: { sizeLimit: '4mb' } } };
vinext silently ignored this, hard-coding a 1 MB limit and always
parsing the body. That created a security downgrade for webhook
handlers (Stripe, GitHub, Slack, etc.) that depend on
'bodyParser: false' to read the raw request stream for HMAC
signature verification. By the time the user handler ran, the body
had already been consumed and parsed into 'req.body', so HMAC
verification would either fail closed (no signature match) or fail
open (handlers that fall back to an empty body for unsupported
content types).
Legitimate large uploads were also rejected with 413 even when the
route declared 'sizeLimit: "4mb"' or higher.
This change introduces a shared helper that resolves the route
module's 'config.api.bodyParser' option and patches both code paths:
- Node/dev path: packages/vinext/src/server/api-handler.ts
- Workers/prod: packages/vinext/src/server/pages-api-route.ts
(+ packages/vinext/src/server/pages-node-compat.ts)
Behaviour now matches Next.js
(packages/next/src/server/api-utils/node/api-resolver.ts):
- bodyParser: false -> skip parsing entirely. Node
handlers receive the raw
IncomingMessage stream;
Workers handlers receive
request.body as a
ReadableStream<Uint8Array>
on 'req.body'.
- bodyParser: { sizeLimit: '4mb' } -> parse with the user-supplied
limit instead of the
hard-coded 1 MB.
- bodyParser: true / undefined -> existing 1 MB default behaviour.
The sizeLimit parser accepts the same suffix grammar Next.js does
('500b', '100kb', '4mb', '1gb'), implemented inline rather than
pulling the 'bytes' package.
Tests cover both Node and Workers paths plus parser-level unit
tests, including:
- bodyParser: false leaves the stream intact for HMAC verification
- sizeLimit '4mb' accepts a 2 MB POST that used to 413
- sizeLimit '4mb' rejects a 5 MB POST with 413
- sizeLimit '100kb' rejects a 200 KB POST with 413
- No config preserves the 1 MB default
config.api.responseLimit is not in scope for this PR and remains a
follow-up.
Reference: Finding F-PROD-4 in SECURITY-AUDIT-2026-05.md
* chore(security): drop unused ResolvedBodyParserConfig export to satisfy knip