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

toPlainMessage doesn't work with RSC and messages that contain binary data (Uint8Array) #531

Closed
fubhy opened this issue Jul 24, 2023 · 6 comments

Comments

@fubhy
Copy link
Contributor

fubhy commented Jul 24, 2023

I tried to utilize toPlainMessage to pass the server<>client boundary in a Next.js application (with React Server Components, RSC).

I ran into an obscure error there which appears to be caused by my messages containing WASM code (binary as Uint8Array).

I confirmed that "rehydrating" the same message (once via binary, once via plain message) doesn't yield the same result:

// server.ts:
function MyServerComponent() {
  return <MyComponent plain={toPlainMessage(message)} binary={message.toBinary()} />
}

// client.ts
function MyClientComponent({ plain, binary }: { plain: PlainMessage, binary: any }) {
  const rehydratedBinary = MyMessageType.fromBinary(Uint8Array.from(binary));
  const rehydratedPlain = new MyMessageType(plain);

  console.log(rehydratedBinary.equals(rehydratedPlain)); // false

  return null;
}

However, it works fine for me if I use .toBinary() on the server and then "rehydrated" on the client using serde utils like this (using base64 to optimize for wire size):

import { Message, MessageType, protoBase64 } from "@bufbuild/protobuf";

export type SerializedMessage<TMessage extends Message<TMessage>> = string & { __type: TMessage };

export function serializeMesage<TMessage extends Message<TMessage>>(message: TMessage) {
  return protoBase64.enc(message.toBinary()) as SerializedMessage<TMessage>;
}

export function deserializeMessage<TMessage extends Message<TMessage>>(
  type: MessageType<TMessage>,
  value: SerializedMessage<TMessage>,
) {
  return type.fromBinary(protoBase64.dec(value));
}
@jcready
Copy link

jcready commented Jul 25, 2023

React Server Components require that all props are serializable to JSON. Your solution of passing the base64 encoded binary is valid, but you could probably simplify the process by just passing down the MyMessageType.toJson() in the props and then use MyMessageType.fromJson() inside the component. The toJson method already handles converting bytes (binary) message fields into their base64 encoded representation to make it serializable to JSON.

@fubhy
Copy link
Contributor Author

fubhy commented Jul 25, 2023

Yeah. I've used .toJson() before. I only added this as an optimization because my messages are insanely large (several megabytes). Using base64 actually saves me a couple hundred kilobytes here.

(My messages are basically small programs for remote execution with all sorts of metadata attached to them)

@fubhy
Copy link
Contributor Author

fubhy commented Jul 25, 2023

Btw. I'm merely raising this issue because toPlainMessage was essentially added as a solution for RSC (see #511 ). But it has it's limitations evidently.

@timostamm
Copy link
Member

@srikrsna-buf, this should be resolved by #533, right?

@srikrsna-buf
Copy link
Member

Yes it can be closed

@fubhy fubhy closed this as completed Aug 30, 2023
@srikrsna-buf
Copy link
Member

srikrsna-buf commented Aug 30, 2023

v1.3.1 has just been released with the fix.

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

No branches or pull requests

4 participants