Message serialization and classes vs. plain javascript objects with React Server Components

This issue has been tracked since 2023-03-02.

Is your feature request related to a problem? Please describe.
Today @bufbuild deserialized grpc responses into generated classes based on protobuf. This makes using React Server Components somewhat cumbersome because the objects are not JSON.stringify serializable. Additionally, available serialization methods are relatively untyped. There is no method to produce a PlainMessage<MessageType> which might (?) work with JSON.stringify. Also, toJson produces a JsonValue instead of some type related to the original generated type (e.g. JsonValue<MessageType> similar to PlainMessage<MessageType>).

Here is a concrete example of what we're currently doing today to workaround this:

page.tsx (using [email protected])

export default function Page() {
  const answer = await eliza.say({sentence: "I feel happy."});

  return <AnswerViewWrapper answer={answer.toJson()} />;
}

AnswerViewWrapper.tsx

export interface AnswerViewWrapperProps {
  readonly answer: JsonValue;
}

export function AnswerViewWrapper({ answer }: AnswerViewWrapperProps) {
  return <AnswerView answer={SayResponse.fromJson(answer)} />;
}

AnswerView.tsx

export interface AnswerViewProps {
  readonly answer: SayResponse;
}

export function AnswerView({ answer }: AnswerViewProps) {
  return <span>{answer.sentence}</span>;
}

Describe the solution you'd like
Eliminate the need to break type safety, and ideally eliminate the extra serialize/deserialize step. Some options:

  1. Move away from, or have an option for, generating plain javascript objects instead of classes. I'm sure there was a strong rationale for using classes, but generally, I believe that a more functional approach will lead to better compatibility across the stack. Use plain interfaces/data objects and provide functions for manipulating them rather than methods.
  2. Provide better type-safe serialization/deserialization. E.g. toJson(): JsonValue<SayResponse> or toPlainMessage(): PlainMessage<SayResponse>

Describe alternatives you've considered

  • Use grpc-js or some other library. IIRC it has the same issues with class based objects, and overall poorer support.

Additional context
Add any other context or screenshots about the feature request here.

smaye81 wrote this answer on 2023-03-08

Hi @dicarlo2. Can you provide a bit more detail on your setup? Are you using Next.js with a custom server? With API Routes? We plan to offer better support for Next.js, so I want to get a better understanding of your environment.

DustinJSilk wrote this answer on 2023-03-14

Im experiencing this issue as well. Im using Qwik framework which can only use serializable values in a response from the server.
I can simply return MyMessage.toJson(), however as mentioned this breaks type safety. There isn't any easily accessible interface to cast it as either.

I've created a helper function as a workaround:

type JsonValues<T> = { [P in keyof T as T[P] extends Function ? never : P]: T[P] }

export function toJson<T extends Message>(msg: T) {
  return msg.toJson() as JsonValues<T>
}
More Details About Repo
Owner Name bufbuild
Repo Name connect-es
Full Name bufbuild/connect-es
Language TypeScript
Created Date 2022-02-16
Updated Date 2023-03-24
Star Count 852
Watcher Count 20
Fork Count 33
Issue Count 17

YOU MAY BE INTERESTED

Issue Title Created Date Updated Date