Repository navigation
feat(fetch, node)!: drop the content-type of an empty body - #178
Conversation
`toFetchBody` and `toNodeHttpBody` now drop a given `content-type` when the body is `undefined`, like the peer adapter, instead of keeping it and adding `standard-server: none`. An empty body needs no hint, so it reads back as `undefined` from the content headers alone, even where the receiver can't see `standard-server` (e.g. a cross-origin browser client). Keeping it bought nothing for HEAD either: the hint and the cleared content-length already made the headers differ from GET. To send headers without content, send an empty Blob with `standard-server: none` (and `content-disposition: []`); for HEAD, send the same body as GET. Express 4/5, Fastify, h3, Hono, srvx, node:http/https/http2 and Bun.serve all drop a HEAD response's bytes and keep its headers, including content-length. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VVaw7QvHcuoeP7ZfCahKtA
…-type - Fold the "drops a given content-type" check into the existing `undefined` encoder tests instead of a near-copy next to them. - Remove the fetch-only empty-Blob round-trip test, which re-tested the unchanged Blob branch, and the `undefined as text/html` e2e row, which only asserted what the plain `undefined` row already does. - Say in the encoder comment why an empty body drops the content-type, unlike the other bodies, instead of repeating the README. - Shorten the README note and drop the HEAD advice. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VVaw7QvHcuoeP7ZfCahKtA
…stream Recommend an already-closed `ReadableStream` with `standard-server: none` instead of an empty Blob: it needs no `content-disposition: []`, and it works as a HEAD response on every HTTP adapter (Express, Fastify, h3, Hono, srvx, node:http/https/http2, Bun), reading back as `undefined`. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VVaw7QvHcuoeP7ZfCahKtA
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VVaw7QvHcuoeP7ZfCahKtA
@standard-server/aws-lambda
@standard-server/core
@standard-server/fastify
@standard-server/fetch
@standard-server/node
@standard-server/peer
@standard-server/shared
commit: |
Merging this PR will not alter performance1 warning
Comparing Footnotes
|
There was a problem hiding this comment.
✅ No new issues found.
Reviewed changes
toFetchBody/toNodeHttpBodyempty-body branch — anundefinedbody now clearscontent-type(instead of emittingstandard-server: nonenext to a caller-supplied content-type);standard-serverandcontent-lengthwere already cleared.- Tests — the
undefinedencoder tests now passcontent-type: text/htmlplus astandard-server: filehint and assert both are dropped; the redundantundefined as html/undefined as text/htmlrows were removed. - Core README — documents that an empty body is sent without
content-type, and points to an already-closedReadableStreamfor an empty response carrying custom headers.
I confirmed the receiver side still resolves none from the cleared headers (no content-type, content-length absent/0 → none, packages/core/src/utils.ts:123), that the peer adapter already drops content-type for undefined (packages/peer/src/body.ts:174), and that fastify/aws-lambda route through toNodeHttpBody. Full suite passes (1485 tests).
deepseek-v4.1-flash (free via Pullfrog for OSS) | 𝕏
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VVaw7QvHcuoeP7ZfCahKtA
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
`@standard-server` adapters drop the `content-type` of an `undefined` body (middleapi/standard-server#178), so the handler codec no longer special-cases it. Also import `ORPCError` in the link decoder docs example. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VVaw7SJxePN5EdCgmjsAN5

Summary
toFetchBodyandtoNodeHttpBodynow drop acontent-typegiven with anundefinedbody, like the peer adapter, instead of keeping it and addingstandard-server: none. Fastify and AWS Lambda pick this up throughtoNodeHttpBody.An empty body has no media type, so it now reads back as
undefinedfrom the content headers alone, including where the receiver can't seestandard-server(for example a cross-origin browser client withoutAccess-Control-Expose-Headers). Keeping the header didn't help HEAD responses either: the added hint and the clearedcontent-lengthalready made their headers differ from GET.To send an empty response with your own headers, such as for HEAD, use an already-closed
ReadableStream. The core README now says so.Changes
packages/fetch/src/body.ts,packages/node/src/body.ts: theundefinedbranch clearscontent-typeand no longer callsgetRequiredStandardBodyHint('none', …).undefinedencoder tests now pass acontent-typeand hint that must be dropped. Removed theundefined as htmlrows from the "keeps a given content-type" tables, and theundefined as text/htmle2e row, which only asserted the body and is covered by the plainundefinedrow.packages/core/README.md: one sentence on empty bodies.Breaking change
A
content-typeset with anundefinedbody is no longer sent. This reverts the empty-body part of #176 (released in v0.12.0).Notes for reviewers
ReadableStreamas a GET and HEAD response over Express 4/5, Fastify, h3, Hono, srvx, node:http/https/http2 and Bun.serve. Every transport answered without errors and kept the custom headers.end(), and the response is piped from the whole stream first, so an endless stream never responds. A closed stream is unaffected. Not fixed here.Testing
undefinedencoder tests fail onmainand pass here.vitest run(1485 tests), the Bun tests, anduncheck(sherif, oxlint, oxfmt, tsc) pass locally.Generated by Claude Code