Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Conversation

@kamalkumar789
Copy link

@kamalkumar789 kamalkumar789 commented Aug 19, 2025

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

lib/reply.js Outdated
return this
if (typeof payload === 'string') {
const contentType = this[kReplyHeaders]['content-type']
if (contentType && contentType.startsWith('application/json')) {
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
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') {
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
if (typeof payload === 'string') {
if (typeof payload === 'string' && payload.length) {

lib/reply.js Outdated
Comment on lines 186 to 188
if (!(payload.startsWith('"') && payload.endsWith('"'))) {
payload = JSON.stringify(payload)
}
Copy link
Member

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?

Suggested change
if (!(payload.startsWith('"') && payload.endsWith('"'))) {
payload = JSON.stringify(payload)
}
if (!(payload[0] === '"' && payload[payload.length - 1] === '"')) {
payload = JSON.stringify(payload)
}

Copy link
Author

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.

@kamalkumar789
Copy link
Author

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) {
Copy link
Member

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.

Copy link
Author

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.

@kamalkumar789
Copy link
Author

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)
Copy link
Member

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

Copy link
Author

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?

Copy link
Member

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

@mcollina mcollina added semver-major Issue or PR that should land as semver major and removed semver-major Issue or PR that should land as semver major labels Aug 26, 2025
@kamalkumar789
Copy link
Author

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.

@kamalkumar789 kamalkumar789 requested a review from Eomm August 26, 2025 12:53
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')) {
Copy link
Contributor

Choose a reason for hiding this comment

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

i dislike the startswith.

Copy link
Author

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.

@kamalkumar789 kamalkumar789 requested a review from Uzlopak August 26, 2025 22:39
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.

6 participants