Skip to content

fix(response): properly serialize Array subclasses like Bun SQL results#1675

Open
raunak-rpm wants to merge 1 commit intoelysiajs:mainfrom
raunak-rpm:fix/bun-sql-serialization-1656
Open

fix(response): properly serialize Array subclasses like Bun SQL results#1675
raunak-rpm wants to merge 1 commit intoelysiajs:mainfrom
raunak-rpm:fix/bun-sql-serialization-1656

Conversation

@raunak-rpm
Copy link

@raunak-rpm raunak-rpm commented Jan 14, 2026

Summary

Fixes #1656

Problem

When returning an Array subclass (e.g., Bun SQL query results), Elysia incorrectly returned [object Object][object Object] instead of valid JSON.

Example from issue:

const app = new Elysia()
    .get("/", async () => {
        const users = await sql`SELECT * FROM users`;  // Bun SQL returns Array subclass
        return users;
    })

Expected: [{"id":1,"name":"Alice"},{"id":2,"name":"Bob"}]
Actual: [object Object][object Object]

Root Cause

The response handlers (mapResponse, mapEarlyResponse, mapCompactResponse) used response?.constructor?.name === 'Array' to detect arrays:

switch (response?.constructor?.name) {
    case 'Array':
    case 'Object':
        return new Response(JSON.stringify(response), ...)
    // ...
    default:
        return new Response(response as any)  // <- Falls here for subclasses
}

This check fails for Array subclasses because their constructor.name is the subclass name (e.g., 'SQLResults'), not 'Array'. The response then fell through to new Response(response) which implicitly calls toString(), producing [object Object] for each element.

Proof:

class SQLResults extends Array { ... }
const results = new SQLResults({id: 1}, {id: 2})

results.constructor.name  // 'SQLResults' - NOT 'Array'!
Array.isArray(results)    // true - correctly identifies it

Solution

Added Array.isArray(response) check in the default case of all response mapping functions. This correctly identifies all arrays including subclasses, ensuring proper JSON serialization.

default:
    // ... existing checks ...
    
    // Handle Array subclasses (e.g., Bun SQL results)
    if (Array.isArray(response)) {
        set.headers['content-type'] = 'application/json'
        return new Response(JSON.stringify(response), set as any)
    }

    return new Response(response as any, set as any)

Changes

  • src/adapter/bun/handler.ts: Add Array.isArray check in mapResponse, mapEarlyResponse (both branches), and mapCompactResponse default cases
  • src/adapter/web-standard/handler.ts: Same changes for web standard adapter
  • test/response/array-subclass.test.ts: Add 10 comprehensive tests

Testing

  • ✅ All 1456 tests pass (10 new tests added)
  • ✅ Verified with simulated Bun SQL results
  • ✅ Content-Type header correctly set to application/json
  • ✅ No regressions for regular arrays and objects

Test Coverage:

  • SQL result simulation (SQLResults class)
  • ORM result sets (ORMResultSet class)
  • Array subclass with set headers
  • Empty Array subclass
  • Array subclass in beforeHandle (mapEarlyResponse)
  • Nested arrays and objects
  • Regular arrays still work correctly
  • Plain objects still work correctly

Summary by CodeRabbit

  • Bug Fixes

    • Enhanced response handling for Array subclass objects (such as database and ORM results) to ensure they are consistently serialized as JSON with proper content-type headers across all response processing paths.
  • Tests

    • Added comprehensive test coverage for Array subclass serialization scenarios, including header preservation, status overrides, empty collections, nested structures, and various response mapping strategies.

✏️ Tip: You can customize this high-level summary in your review settings.

Fixes elysiajs#1656

Problem:
When returning an Array subclass (e.g., Bun SQL query results), Elysia
incorrectly returned '[object Object][object Object]' instead of valid JSON.

Root Cause:
The response handlers (mapResponse, mapEarlyResponse, mapCompactResponse) used
`response?.constructor?.name === 'Array'` to detect arrays. This check fails
for Array subclasses because their constructor.name is the subclass name (e.g.,
'SQLResults'), not 'Array'. The response then fell through to
`new Response(response)` which implicitly calls toString(), producing
'[object Object]' for each element.

Solution:
Added `Array.isArray(response)` check in the default case of all response
mapping functions. This correctly identifies all arrays including subclasses,
ensuring proper JSON serialization with the correct Content-Type header.

Changes:
- src/adapter/bun/handler.ts: Add Array.isArray check in mapResponse,
  mapEarlyResponse (both branches), and mapCompactResponse default cases
- src/adapter/web-standard/handler.ts: Same changes for web standard adapter
- test/response/array-subclass.test.ts: Add 10 comprehensive tests covering
  SQL result simulation, ORM result sets, nested data, and edge cases

Testing:
- All 1456 tests pass (10 new tests added)
- Verified with simulated Bun SQL results that previously returned
  '[object Object][object Object]' now correctly return JSON
@pkg-pr-new
Copy link

pkg-pr-new bot commented Jan 14, 2026

Open in StackBlitz

npm i https://pkg.pr.new/elysiajs/elysia@1675

commit: 5587569

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 14, 2026

Walkthrough

This PR adds explicit handling for Array subclasses across response-mapping functions in both Bun and web-standard adapters. When responses are Arrays, the code now returns JSON-stringified data with proper content-type headers, addressing a bug where Bun SQL results and similar Array subclasses were not being correctly serialized.

Changes

Cohort / File(s) Summary
Response Handlers
src/adapter/bun/handler.ts, src/adapter/web-standard/handler.ts
Adds Array.isArray() checks in mapResponse, mapEarlyResponse (multiple branches), and mapCompactResponse to explicitly serialize Array subclasses as JSON with application/json content-type, preventing fallthrough to generic response handling
Array Subclass Tests
test/response/array-subclass.test.ts
New comprehensive test suite covering JSON serialization of Array subclasses (SQLResults, ORMResultSet), header/status handling, empty arrays, nested structures, and integration with mapEarlyResponse scenarios

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Poem

🐰 Array subclasses hop with glee,
Now JSON-stringified, clear as can be!
Bun's SQL results dance free,
With proper headers for all to see! 🎉
No more [object Object] spree!

🚥 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 title accurately and concisely describes the main fix: proper serialization of Array subclasses like Bun SQL results.
Linked Issues check ✅ Passed The changes directly address issue #1656 by adding Array.isArray() checks to detect and JSON-serialize Array subclasses with correct Content-Type headers.
Out of Scope Changes check ✅ Passed All changes are scoped to fixing Array subclass serialization in response handlers and adding comprehensive tests; no unrelated modifications detected.
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 docstrings


📜 Recent review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f027642 and 5587569.

📒 Files selected for processing (3)
  • src/adapter/bun/handler.ts
  • src/adapter/web-standard/handler.ts
  • test/response/array-subclass.test.ts
🔇 Additional comments (11)
src/adapter/bun/handler.ts (4)

143-148: LGTM! Correct fix for Array subclass detection.

The Array.isArray() check properly detects Array subclasses that constructor.name === 'Array' misses. Placement in the default branch after other type checks is appropriate.


288-293: LGTM!

Correctly handles Array subclasses in the mapEarlyResponse branch where headers/status/cookie are set.


412-418: LGTM!

Correctly handles Array subclasses in the mapEarlyResponse else branch, creating a new headers object since set isn't being used in this path.


537-543: LGTM!

Correctly handles Array subclasses in mapCompactResponse, following the same pattern as the existing charCodeAt JSON detection block above.

test/response/array-subclass.test.ts (3)

17-25: LGTM! Well-designed test fixture.

The SQLResults class correctly simulates Bun SQL's Array subclass behavior using Object.setPrototypeOf to ensure proper prototype chain. This is the recommended pattern for extending built-in types like Array.


28-38: LGTM!

Good representation of ORM-style result sets. The query and duration metadata properties correctly won't appear in the JSON output since JSON.stringify on arrays only serializes indexed elements, matching real-world ORM behavior.


40-213: Comprehensive test coverage for the fix.

The tests effectively cover:

  • All three response mapping functions (mapResponse, mapEarlyResponse, mapCompactResponse)
  • Edge cases (empty arrays, nested structures)
  • Header propagation and status code handling
  • Regression tests ensuring plain arrays/objects still work
src/adapter/web-standard/handler.ts (4)

175-180: LGTM!

Correctly mirrors the fix in bun/handler.ts for the web-standard adapter's mapResponse function.


321-326: LGTM!

Correctly handles Array subclasses in mapEarlyResponse with set headers/status/cookie.


446-452: LGTM!

Correctly handles Array subclasses in the mapEarlyResponse else branch with standalone headers object.


575-581: LGTM!

Correctly handles Array subclasses in mapCompactResponse, completing the fix across all response mapping paths.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.


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.

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.

Returning non-JSON serializable response when using Elysia with Bun SQL

2 participants