Skip to content

Conversation

@yusukebe
Copy link
Member

@yusukebe yusukebe commented Sep 23, 2025

This PR will fix the problem that we wanted to resolve with #4382.

Problem

The following code will cause the error:

import { Hono } from 'hono'

const app = new Hono()

app.post('/', async (c) => {
  const data = await c.req.json()
  await c.req.raw.json() // Body already used!
  return c.json(data)
})

export default app
CleanShot 2025-09-23 at 17 17 54@2x

This is because when you do c.req.raw, the body to be used is already consumed. This is explained in #4382 (comment).

Solution

In this PR, it will create a new Request object with a cached body when c.req.raw is called. The reason why it does not use getter/setter and uses Object.defineProperty is that if it were to use getter/setter, it would fail with the spread syntax {...c.req}.

Caveat

The code size will be increased.

The performance may degrade, but it's small.

The author should do the following, if applicable

  • Add tests
  • Run tests
  • bun run format:fix && bun run lint:fix to format the code
  • Add TSDoc/JSDoc to document the code

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions
Copy link

Bundle size check

main (393ded9) #4425 (eacb440) +/-
Bundle Size (B) 18,294B 18,557B 263B
Bundle Size (KB) 17.87K 18.12K 0.25K

Compiler Diagnostics (tsc)

main (393ded9) #4425 (eacb440) +/-
Files 300 300 0
Lines 140,207 140,228 21
Identifiers 127,056 127,083 27
Symbols 264,250 264,292 42
Types 155,889 155,935 46
Instantiations 538,872 538,826 -46
Memory used 310,269K 308,090K -2,179K
I/O read 0.02s 0.03s 0.01s
I/O write 0s 0s 0s
Parse time 0.72s 0.78s 0.06s
Bind time 0.3s 0.3s 0s
Check time 2.67s 2.77s 0.1s
Emit time 0s 0s 0s
Total time 3.69s 3.86s 0.17s

Compiler Diagnostics (typescript-go)

main (393ded9) #4425 (eacb440) +/-
Files 269 269 0
Lines 118,096 118,117 21
Identifiers 116,489 116,516 27
Symbols 353,957 353,972 15
Types 277,831 277,878 47
Instantiations 3,528,587 3,528,359 -228
Memory used 223,102K 223,175K 73K
Memory allocs 10,491,846 10,491,870 24
Parse time 0.07s 0.078s 0.008s
Bind time 0.024s 0.016s -0.008s
Check time 1.426s 1.509s 0.083s
Emit time 0s 0s 0s
Total time 1.534s 1.605s 0.071s

Reported by octocov

@github-actions
Copy link

HTTP Performance Benchmark

Framework Runtime Average Ping Query Body
hono (origin/main) bun 38,250.69 53,142.55 33,106.71 28,502.80
hono (current) bun 37,401.78 52,662.68 31,646.26 27,896.40
Change -2.22% -0.90% -4.41% -2.13%

@codecov
Copy link

codecov bot commented Sep 23, 2025

Codecov Report

❌ Patch coverage is 94.28571% with 2 lines in your changes missing coverage. Please review.
✅ Project coverage is 91.28%. Comparing base (393ded9) to head (4c411e6).

Files with missing lines Patch % Lines
src/request.ts 94.28% 2 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #4425      +/-   ##
==========================================
+ Coverage   91.25%   91.28%   +0.02%     
==========================================
  Files         171      171              
  Lines       10913    10933      +20     
  Branches     3144     3149       +5     
==========================================
+ Hits         9959     9980      +21     
+ Misses        953      952       -1     
  Partials        1        1              

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copy link
Contributor

@kamaal111 kamaal111 left a comment

Choose a reason for hiding this comment

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

In HonoRequest, where we don't need to access the raw body, can we use the private _raw instead?

This might improve the performance slightly


// Second call to req.raw.json() - should work with cached body
const secondResult = await req.raw.json()
expect(secondResult).toEqual(data)
Copy link
Contributor

Choose a reason for hiding this comment

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

Can you also verify that it still works as expected when try to get the body for the third time?

To switch it up a bit we can also use .text which would still result in getting the cached body and constructing a new Request object

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.

3 participants