-
-
Notifications
You must be signed in to change notification settings - Fork 2.5k
fix: serialize string payloads as JSON in reply.send #6296
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
fix: serialize string payloads as JSON in reply.send #6296
Conversation
lib/reply.js
Outdated
| return this | ||
| if (typeof payload === 'string') { | ||
| const contentType = this[kReplyHeaders]['content-type'] | ||
| if (contentType && contentType.startsWith('application/json')) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| if (contentType && contentType.startsWith('application/json')) { | |
| if (contentType?.startsWith('application/json')) { |
lib/reply.js
Outdated
| if (typeof payload !== 'string') { | ||
| preSerializationHook(this, payload) | ||
| return this | ||
| if (typeof payload === 'string') { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| if (typeof payload === 'string') { | |
| if (typeof payload === 'string' && payload.length) { |
lib/reply.js
Outdated
| if (!(payload.startsWith('"') && payload.endsWith('"'))) { | ||
| payload = JSON.stringify(payload) | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure about this, is this restoring some functionality?
| if (!(payload.startsWith('"') && payload.endsWith('"'))) { | |
| payload = JSON.stringify(payload) | |
| } | |
| if (!(payload[0] === '"' && payload[payload.length - 1] === '"')) { | |
| payload = JSON.stringify(payload) | |
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This line restores the intended behavior so that strings are now correctly serialized if the content type is JSON.
|
The bug is fixed and tested. No need to update documentation. Will be tackled in reply.js. |
| } | ||
| } | ||
|
|
||
| if (this[kReplySerializer] !== null && typeof payload === 'string' && payload.length) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it doesn't prevent double stringify.
You are updating the payload and the next if will serialize it again because the serializer is not empty.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think yes, you are right, but it can be solved with some logic. Let me push new code.
|
It's fixing the problem of double stringify in any way. |
| if (contentType?.startsWith('application/json')) { | ||
| // Avoid double stringify if payload is not already JSON string | ||
| if (!(payload[0] === '"' && payload[payload.length - 1] === '"')) { | ||
| preSerializationHook(this, payload) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is an async stuff, we can't fork the logic here.
we need to reshape a bit the code to find the best place to add this check
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you think this logic or check is fine, can you suggest where I can put or make a separate async function?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will check it for sure - I thought the fix could be easier by it is slippery
|
I tested coverage on main (server.js and fastify/lib) and it’s already below 100%. Regarding the new logic I added: it introduces an inner if that only runs when the payload is a non-empty string, content-type starts with application/json, and the string is not already quoted. Covering this would require adding a new test case specifically for this scenario, which I haven’t added. Also, the CI workflow failed during pnpm/action-setup with a self-installer error (Error: Something went wrong, self-installer exits with code 1). This appears unrelated to my changes and may need attention from maintainers. |
lib/reply.js
Outdated
|
|
||
| // Check if a serializer exists and payload is a non-empty string | ||
| if (this[kReplySerializer] !== null && typeof payload === 'string' && payload.length) { | ||
| if (contentType?.startsWith('application/json')) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i dislike the startswith.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changed to use indexOf instead of startsWith for more robust content-type checking.
This fix addresses the issue with the reply.send(string) does not serialize strings as JSON even when the Content-Type is set to application/json. Previously, Fastify would send the string raw, which did not match the documented behavior. The change ensures that if the payload is a string and the content type is JSON, it is properly wrapped with quotes using JSON.stringify, while avoiding double serialization if the string is already in JSON format. This restores the expected behavior without affecting other payload types or custom serializers.
after calling this,
reply
.type('application/json; charset=utf-8')
.send("Hello")
Fastify will automatically serialize "Hello" as JSON (""Hello"" in the response body) behind the scenes. There’s no need for the user to manually call JSON.stringify or set a custom serializer—the condition you added handles it.
Fix is in reply.js