Skip to content
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

JSON in RPC is not being sent with the correct Content-Type headers (even other custom headers not working) #3814

Open
DDuran19 opened this issue Jan 9, 2025 · 8 comments
Labels

Comments

@DDuran19
Copy link

DDuran19 commented Jan 9, 2025

What version of Hono are you using?

4.6.16

What runtime/platform is your app running on? (with version if possible)

local - nodejs. remote - cloudflare workers

What steps can reproduce the bug?

Create an endpoint and use zValidator("json", someValidator)
create an RPC that client can use. use hc
access the endpoint and pass in a json body
check the network tab and you would see the content-type is text/plain instead of application/json

What is the expected behavior?

headers should be Content-Type = application/json

What do you see instead?

text/plain

Additional information

client rpc
image

endpoint:
image

error returned by zValidator cause he's not able to parse the body correctly.
image

correct payload was sent
image

incorrect headers were set
image

@DDuran19 DDuran19 added the triage label Jan 9, 2025
@DDuran19
Copy link
Author

DDuran19 commented Jan 9, 2025

I think the main problem here is in the implementation of hc. I am not sure, I can't seem to figure it out I've been having this trouble for a few months now. The JSON used to work but I don't remember which update broke it.

@devpapi0891
Copy link

Up

@EdamAme-x
Copy link
Contributor

EdamAme-x commented Jan 9, 2025

I cannot reproduce this.
The server-side validator does not affect the client's behavior, so the cause must be the client.

If you don't mind, I'd like to know more about the situation. (for example, initialization code)
I will try a few things.

@EdamAme-x
Copy link
Contributor

Example Code

import { hc } from "./client";

const c = hc("http://localhost", {
    fetch: console.log
})

c.api.forms.$post({
    json: {
        name: "a",
        description: "b",
        entity: [],
        public: ""
    }
})

Log

http://localhost/api/forms {
  body: "{\"name\":\"a\",\"description\":\"b\",\"entity\":[],\"public\":\"\"}",
  method: "POST",
  headers: Headers {
    "content-type": "application/json",
  },
}

On Node, Deno, Bun

@yusukebe
Copy link
Member

yusukebe commented Jan 14, 2025

Hi @DDuran19

I can't reproduce it. The following are working correctly:

The server:

import { Hono } from 'hono'
import { zValidator } from '@hono/zod-validator'
import { z } from 'zod'

const app = new Hono()

export const routes = app.post(
  '/',
  zValidator(
    'json',
    z.object({
      foo: z.string()
    })
  ),
  (c) => {
    return c.json(c.req.header())
  }
)

export default app

The client:

import { hc } from 'hono/client'
import { routes } from './server'

const client = hc<typeof routes>('http://localhost:3000')
const res = await client.index.$post({
  json: {
    foo: 'bar'
  }
})

const data = await res.json()
console.log(data['content-type']) // application/json

Maybe it's not a bug.

@yusukebe yusukebe added not bug and removed triage labels Jan 14, 2025
@DDuran19
Copy link
Author

Sorry for the late response, I also cannot replicate it. We are using svelte 5, superforms and zod. I am not sure why that is happening, it just wouldn't work.. what could be the reason for it?

could it be because of my modifiedFetch? but I don''t think it's the reason cause I tried replacing that with simple console.log("fetch_start") and console.log("fetch_end")`. but it still misbehaves...

Is there a way that I as a coder mess up something on the way it tracks what the header should be?

export const modifiedFetch: typeof globalThis.fetch = async (input, init) => {
	try {
		globalEventEmitter.emit('fetch_start', null);
		const response = await fetch(input, init);
		return response;
	} finally {
		globalEventEmitter.emit('fetch_end', null);
	}
};

export const clientFetch = hc<Routes>('/', {
	fetch: modifiedFetch
});
import { dev } from '$app/environment';

export type Listener<T = any> = (data: T) => Promise<void> | void;

export abstract class BaseEventEmitter<Events extends Record<string, any>> {
	abstract listeners: Map<keyof Events, Set<Listener<any>>>;

	on<T extends keyof Events, V extends Events[T]>(event: T, listener: Listener<V>): void {
		if (!this.listeners) {
			this.listeners = new Map();
		}

		if (!this.listeners.has(event)) {
			this.listeners.set(event, new Set());
		}
		this.listeners.get(event)?.add(listener);
	}

	off<T extends keyof Events, V extends Events[T]>(event: T, listener: Listener<V>): void {
		if (this.listeners.has(event)) {
			this.listeners.get(event)?.delete(listener);
		}
	}

	once<T extends keyof Events, V extends Events[T]>(event: T, listener: Listener<V>): void {
		const onceListener: Listener<V> = (data: V) => {
			this.off(event, listener);
			listener(data);
		};
		this.on(event, onceListener);
	}

	emit<T extends keyof Events, V extends Events[T]>(event: T, data: V): void {
		this._emit(event);
		const listeners = this.listeners.get(event);
		if (listeners && listeners.size > 0) {
			listeners.forEach(async (l) => {
				try {
					await l(data);
				} catch (error) {
					console.error('Error in listener:', error);
				}
			});
		}
	}

	private _emit(event: keyof Events) {
		if (dev && import.meta.env.VITE_TRACE_EMIT_STACK === 'true') {
			try {
				// @ts-expect-error
				throw new Error(event);
			} catch (error: any) {
				console.error({
					stack: error.stack
				});
			}
		}
	}
}

@yusukebe
Copy link
Member

@DDuran19

Thank you for your explanation. Hmm. I cannot help with the specific issue related to your application. If you think it is a bug, please create a minimum reproducible project.

@DDuran19
Copy link
Author

I will try, thank you @yusukebe I'll reach out once I was able to

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

4 participants