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

feat(client/POC): RPC support for SSE #3957

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from

Conversation

sor4chi
Copy link
Contributor

@sor4chi sor4chi commented Feb 25, 2025

resolve #3309

Todo

  • SSE rpc handler
  • FetchEvent (polyfill?)
    • LICENCE inclusion
  • Typed Data interface for JSON SSE

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

@sor4chi sor4chi changed the title Feat/hono-client-sse feat(client): RPC support for SSE Feb 25, 2025
@sor4chi sor4chi changed the title feat(client): RPC support for SSE feat(client/POC): RPC support for SSE Feb 25, 2025
@sor4chi
Copy link
Contributor Author

sor4chi commented Feb 26, 2025

Hi, @yusukebe

This time, I would like to introduce Typed SSE that bypasses JSON as a feature of SSE RPC. Typed SSE is very useful, so I would like to introduce it.
Currently, the data field of SSE can specify string | Promise<string>, and the reason why it is not simply a string is that #3344 implemented by @usualoma allows Hono Stream SSE to send Hono JSX (HTML).

The problem is with the string. Most Hono Stream SSE users probably specify JSON.stringify in the data field. However, if we implement it straightforwardly, the JSON serialization and deserialization operations will be performed in the Hono RPC. Therefore, the client needs to distinguish between the JSON.stringify data and the data serialized by the RPC as Typed SSE, and we are currently exploring how to implement this while maintaining backward compatibility. If you have any good ideas, I would appreciate it if you could let me know.

@nakasyou
Copy link
Contributor

The problem is with the string. Most Hono Stream SSE users probably specify JSON.stringify in the data field. However, if we implement it straightforwardly, the JSON serialization and deserialization operations will be performed in the Hono RPC. Therefore, the client needs to distinguish between the JSON.stringify data and the data serialized by the RPC as Typed SSE, and we are currently exploring how to implement this while maintaining backward compatibility. If you have any good ideas, I would appreciate it if you could let me know.

How about adding streamJSONRPC and dividing .$sse into .$sse and .$sseJSON?

@sor4chi
Copy link
Contributor Author

sor4chi commented Feb 26, 2025

HI @nakasyou.
While completely separating them is one option, I am concerned about the migration barriers and cognitive load that would increase by doing so.
It is also challenging to figure out how to handle JSON encoded SSE and error events on the user side with the EventSource interface.
One idea is to create a dedicated event like $rpc and only enable type completion when event.type === "$rpc", but this would fix the event and reduce the flexibility of SSE expression.

@yusukebe
Copy link
Member

Hi @sor4chi

I don't want to introduce the polyfill of EventSource Parser. It increases package size and maintenance costs. Can you make it work with extending global objects like globalThis.document? Or any good solution?

@sor4chi
Copy link
Contributor Author

sor4chi commented Feb 28, 2025

I can't think of a way to do this at the moment other than to cut it out as a sub-package and make it load only in environments where EventSource is available, such as a browser, and have the user explicitly switch between the two.

@nakasyou
Copy link
Contributor

It increases package size and maintenance costs.

This is an idea to reduce package size. What do you think of making $sse a function instead of a method. e.g:

import { sse, hc } from 'hono/client'

const client = hc<typeof app>('/')
const source = sse(client.stream.get())

By doing that, the codebase will tree-shakeable. And we will be able to use POST requests to receive streaming. For example, most of LLM APIs such as OpenAI use POST method to stream content. It means that we can't use EventSource. Similarly, there are many users who want to use POST method to stream content using Hono RPC I guess.

@yusukebe
Copy link
Member

yusukebe commented Mar 3, 2025

@sor4chi @nakasyou

Thank you for the comments!

The problem I'm having is not the bundle size but the package size. In other words, the amount of code included in the hono package is increasing (already that is fat).

In this PR, you are using the eventsource and eventsource-parser code, so I think it is almost the same as using external libraries. So, how about making it a 3rd party middleware called @hono/sse-client based on @nakasyou's idea? This can depend on eventsource and eventsource-parser.

import { sse } from '@hono/sse-client'

const client = hc<typeof app>('/')
const source = sse(client.stream.get())

@sor4chi
Copy link
Contributor Author

sor4chi commented Mar 3, 2025

Totally agree. I'll try it!
Thank you so much

@yusukebe
Copy link
Member

yusukebe commented Mar 3, 2025

@sor4chi Thank you for your understanding!

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.

RPC support for SSE
3 participants