Skip to content

fix(node): ignore req.body that express 4's body-parser assigns without parsing - #102

Merged
dinwwwh merged 1 commit into
mainfrom
claude/body-parser-compat-f5feef
Sep 25, 2026
Merged

dinwwwh merged 1 commit into
mainfrom
claude/body-parser-compat-f5feef

Conversation

@dinwwwh

@dinwwwh dinwwwh commented Sep 25, 2026

Copy link
Copy Markdown
Member

Under Express 4, express.json() (body-parser 1.x) sets req.body = {} on every request, even ones it never reads. Because of that, toStandardBody returned {} for file uploads, FormData, URLSearchParams, octet-streams and event-streams, and for a bodyless GET it returned {} where it should return undefined. The adapter now uses req.body only once the request stream has been consumed. Express 4 apps get the correct bodies back, and Express 5 behaves as before.

Fixes

  • Under Express 4 + express.json(), non-JSON request bodies are parsed correctly again instead of resolving to {}
  • Bodyless requests under Express 4 resolve to undefined again

Behavior change

  • If a req.body is set on a request whose stream nobody has read, the adapter now ignores it and parses the stream itself.

Testing

  • Express 4 (with and without the body parser) is added to the data-transfer and signal/cancel integration matrices. Before the fix, 17 of those tests failed; all pass after it.
  • Unit test for a req.body that was assigned but never read, covering a file upload and a bodyless GET
  • To test both majors, adds express4 and @types/express4 as aliased dev dependencies
  • Full suite (1202 tests), tsc -b and eslint pass

…ut parsing

body-parser 1.x (express 4) sets `req.body = {}` on every request, even the
ones it leaves unread, so file, form-data, url-search-params, octet-stream and
event-stream bodies all resolved to `{}`, and a bodyless request gave `{}`
instead of `undefined`. Only trust `req.body` once the request stream has been
consumed.

Adds express 4 to the integration test matrix.
@pkg-pr-new

pkg-pr-new Bot commented Sep 25, 2026

Copy link
Copy Markdown
@standard-server/aws-lambda

npm i https://pkg.pr.new/@standard-server/aws-lambda@102

@standard-server/core

npm i https://pkg.pr.new/@standard-server/core@102

@standard-server/fastify

npm i https://pkg.pr.new/@standard-server/fastify@102

@standard-server/fetch

npm i https://pkg.pr.new/@standard-server/fetch@102

@standard-server/node

npm i https://pkg.pr.new/@standard-server/node@102

@standard-server/peer

npm i https://pkg.pr.new/@standard-server/peer@102

@standard-server/shared

npm i https://pkg.pr.new/@standard-server/shared@102

commit: f955c4e

@codspeed

codspeed Bot commented Sep 25, 2026

Copy link
Copy Markdown
Contributor

Merging this PR will not alter performance

✅ 26 untouched benchmarks
⏩ 108 skipped benchmarks1


Comparing claude/body-parser-compat-f5feef (f955c4e) with main (a6c90ca)

Open in CodSpeed

Footnotes

  1. 108 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports. ↩

@codecov

codecov Bot commented Sep 25, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ No new issues found.

Reviewed changes

  • Node body short-circuit — toStandardBody now trusts an upstream req.body only when !req.readable, so express 4's body-parser {} placeholder is ignored and the stream is parsed instead.
  • Unit test — the existing prefer parsed body case now consumes the stream first, and a new case asserts a req.body = {} set without consuming is ignored for both a file upload and a bodyless GET.
  • Express 4 integration matrix — createExpressjsClientServerTest gains version?: 4 | 5; express 4 (with and without the body parser) is added to the data-transfer and signal/cancel matrices.
  • Dev dependencies — aliased express4 / @types/express4 and the corresponding lockfile entries.

I confirmed the premise empirically (Node 24, express 4.22.3 and 5.2.1): body-parser 1.x sets req.body = {} with readable === true for content types it leaves unread, while after parsing JSON it sets readable === false; body-parser 2.x leaves req.body undefined. Node's readable is true before any read (even bodyless GET) and false only after full consumption, so !req.readable is a correct proxy for "consumed" on these paths. Reverting the guard makes the expressjs4-body-parser integration cases fail, so the new coverage is real. Local runs: body.test.ts 35 pass, data-transfer expressjs 64 pass, signal-and-cancel expressjs 68 pass, root tsc and eslint on the changed files clean.

The intentional behavior change (ignore a req.body whose stream was never read) is the point of the fix and is already documented in the PR body. No action needed.

Pullfrog  | View workflow run | Using DeepSeek Flash (default — pick a model for stronger reviews) | 𝕏

@dinwwwh
dinwwwh merged commit 4488969 into main Sep 25, 2026
11 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant