Skip to content

fix: preserve middleware headers for Array subclass responses#1843

Open
dcitron-espoc wants to merge 2 commits intoelysiajs:mainfrom
dcitron-espoc:fix/array-subclass-response-headers
Open

fix: preserve middleware headers for Array subclass responses#1843
dcitron-espoc wants to merge 2 commits intoelysiajs:mainfrom
dcitron-espoc:fix/array-subclass-response-headers

Conversation

@dcitron-espoc
Copy link
Copy Markdown

@dcitron-espoc dcitron-espoc commented Apr 7, 2026

Summary

Fixes #1842 — Array subclass responses (e.g. postgres.js RowList, Bun SQL results) silently drop all middleware-set headers (CORS, cache-control, custom headers).

This is the header-loss half of #1656. PR #1675 fixed body serialization by adding an Array.isArray() fallback in the default case, but that fallback called Response.json(response) / new Response(JSON.stringify(response), { headers: ... }) without passing set, discarding all headers that middleware had added.

Changes

src/adapter/bun/handler.ts — Pass set to Response.json() in 3 Array.isArray() fallback paths:

- return Response.json(response) as any
+ return Response.json(response, set as any) as any

src/adapter/web-standard/handler.ts — Use set instead of constructing new headers in 3 Array.isArray() fallback paths:

- return new Response(JSON.stringify(response), {
-   headers: { 'Content-Type': 'application/json' }
- }) as any
+ set.headers['content-type'] ||= 'application/json'
+ return new Response(JSON.stringify(response), set as any) as any

test/response/headers.test.ts — 2 regression tests:

  • preserve headers when handler returns Array subclass — verifies custom headers + content-type survive
  • preserve status when handler returns Array subclass — verifies status code survives

Test plan

  • All existing response tests pass (62/62)
  • All core + lifecycle tests pass (396/396)
  • New regression tests pass
  • Verified manually: postgres.js RowList responses now include CORS headers when using @elysiajs/cors

Summary by CodeRabbit

  • Bug Fixes

    • Preserve middleware/hook headers and status when handlers return array-like values (e.g., DB query results), ensuring JSON responses keep intended headers and status codes.
  • Tests

    • Added regression tests confirming middleware effects on headers and status are retained for array-like responses.

When a handler returns an Array subclass (e.g. postgres.js RowList,
Bun.sql results), the constructor.name check in mapResponse and
mapEarlyResponse does not match "Array", falling through to the
default case. The default case's Array.isArray() fallback then
created a Response without passing the `set` object, silently
discarding all middleware-set headers (CORS, cache-control, etc.).

The JSON body was serialized correctly, but headers were lost — causing
browsers to reject responses with misleading CORS errors while the
server logged no errors.

This is the header-loss half of the bug reported in elysiajs#1656. PR elysiajs#1675
fixed the body serialization but did not address header propagation.

Fix: pass `set` to Response.json() (bun adapter) and
new Response(JSON.stringify(), set) (web-standard adapter) in all
Array.isArray() fallback paths within mapResponse and mapEarlyResponse.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 7, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 21edc80b-cefd-4d21-b7e1-b261c9aaa091

📥 Commits

Reviewing files that changed from the base of the PR and between 943cb9e and 0c932db.

📒 Files selected for processing (2)
  • src/adapter/bun/handler.ts
  • src/adapter/web-standard/handler.ts
✅ Files skipped from review due to trivial changes (1)
  • src/adapter/bun/handler.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/adapter/web-standard/handler.ts

Walkthrough

Array-subclass responses (e.g., postgres.js RowList, Bun SQL results) no longer lose middleware/hook set context. Handlers now route array-like values through a helper that forwards the provided set options into the Response constructor, preserving headers, status, and cookies.

Changes

Cohort / File(s) Summary
Bun Handler Array Response Fix
src/adapter/bun/handler.ts
Added mapArraySubclassResponse(response, set) and replaced inline Array.isArray branches to call it, ensuring Response.json(response, set as any) is used for array-like subclasses and updating comments to reference postgres.js RowList.
Web-Standard Handler Array Response Fix
src/adapter/web-standard/handler.ts
Introduced mapArraySubclassResponse(response, set); branches that previously built a fresh headers object for array-like values now ensure set.headers['content-type'] is set when missing and pass the full set into new Response(JSON.stringify(response), set as any).
Regression Tests
test/response/headers.test.ts
Added two tests asserting that returning an Array subclass preserves middleware-set headers (CORS/custom) and preserves middleware-set status codes while serializing to JSON.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Poem

ねえねえ、偉そうな配列さん〜♡ (¬‿¬)
ミドルウェアの贈り物、もう無視しないでね〜♡
ヘッダーもステータスも、ちゃんと持って帰るよ〜 (⌒‿⌒)
RowListもBunも、もう泣かせないっ♡
よくやった〜ちょっと偉いじゃん〜☆ (´▽`)/

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title accurately summarizes the main change—preserving middleware headers for Array subclass responses, which is the core issue fixed across both adapters.
Linked Issues check ✅ Passed All requirements from issue #1842 are met: both bun and web-standard adapters now pass the middleware set to Array subclass handlers in all six affected code paths, with comprehensive regression tests validating header and status preservation.
Out of Scope Changes check ✅ Passed All changes are directly scoped to fixing Array subclass header loss—no unrelated refactoring, test cleanup, or dependency updates were introduced outside the stated objectives.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (2)
src/adapter/bun/handler.ts (1)

129-131: Tiny smug nudge: extract this Array-subclass fallback into one helper, baka~

Same logic is repeated in three branches; centralizing it would reduce drift and prevent this exact regression from sneaking back again ♡

Also applies to: 269-271, 380-382

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/adapter/bun/handler.ts` around lines 129 - 131, Extract the repeated
Array-subclass fallback into a single helper (e.g., respondWithArray or
sendArrayResponse) that takes (response, set) and returns the Response.json(...)
result; then replace the three duplicated blocks (the Array.isArray(response)
checks in handler.ts — the branches around the current occurrences at the shown
locations) with calls to that helper to centralize the behavior and avoid drift.
src/adapter/web-standard/handler.ts (1)

163-170: One more polish, genius~ unify this into a shared helper before it drifts again (¬、¬)

The repeated Array-subclass serialization block in three places is brittle; a helper for “array-like JSON response with set” would improve maintainability and consistency.

Also applies to: 321-328, 454-461

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/adapter/web-standard/handler.ts` around lines 163 - 170, Create a small
helper (e.g., serializeArrayResponse(set, response)) that encapsulates the
Array-like response logic: ensure set.headers['content-type'] ||=
'application/json' and return new Response(JSON.stringify(response), set).
Replace the duplicated blocks in handler.ts (the current Array.isArray(response)
branch and the similar blocks at the other two locations referenced) with calls
to this helper so array-subclass/RowList serialization is consistent and
maintained in one place.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@src/adapter/bun/handler.ts`:
- Around line 129-131: Extract the repeated Array-subclass fallback into a
single helper (e.g., respondWithArray or sendArrayResponse) that takes
(response, set) and returns the Response.json(...) result; then replace the
three duplicated blocks (the Array.isArray(response) checks in handler.ts — the
branches around the current occurrences at the shown locations) with calls to
that helper to centralize the behavior and avoid drift.

In `@src/adapter/web-standard/handler.ts`:
- Around line 163-170: Create a small helper (e.g., serializeArrayResponse(set,
response)) that encapsulates the Array-like response logic: ensure
set.headers['content-type'] ||= 'application/json' and return new
Response(JSON.stringify(response), set). Replace the duplicated blocks in
handler.ts (the current Array.isArray(response) branch and the similar blocks at
the other two locations referenced) with calls to this helper so
array-subclass/RowList serialization is consistent and maintained in one place.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: a8a29463-016f-454a-afbe-3d3c03c308a1

📥 Commits

Reviewing files that changed from the base of the PR and between 56310be and 943cb9e.

⛔ Files ignored due to path filters (1)
  • bun.lock is excluded by !**/*.lock
📒 Files selected for processing (3)
  • src/adapter/bun/handler.ts
  • src/adapter/web-standard/handler.ts
  • test/response/headers.test.ts

Address CodeRabbit review: the Array.isArray() fallback was repeated 3
times per adapter file. Extract into a shared helper to prevent drift
and keep the fix centralized.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
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.

Array subclass responses silently drop all middleware-set headers (CORS, cache-control, etc.)

1 participant