connectrpc / connect-es

The TypeScript implementation of Connect: Protobuf RPC that works.
https://connectrpc.com/
Apache License 2.0
1.39k stars 82 forks source link

Nextjs 15: Failed to generate cache key for all unary calls in RSC #1326

Open mrthinger opened 2 weeks ago

mrthinger commented 2 weeks ago

When calling a unary method using @connectrpc/connect-web in a React server component in Next.js 15, you will always get the following error:

Failed to generate cache key for https://demo.connectrpc.com/connectrpc.eliza.v1.ElizaService/Say

Example: https://github.com/connectrpc/examples-es/blob/938b4cbd7050bd2e7f73f31ccc89dabfc3443e3b/nextjs/app/react-server-actions/page.tsx#L22

To Reproduce node 22

git clone --depth 1 --branch main https://github.com/connectrpc/examples-es.git
cd examples-es
git fetch --depth 1 origin 2b0dee9557f3e596e3dee326a173f443c4a8663a
git checkout 2b0dee9557f3e596e3dee326a173f443c4a8663a
cd nextjs
npm install
npm run generate
npm run start

Once the server is running:

  1. Open http://localhost:3000/react-server-actions in your browser
  2. Enter some text in the input field
  3. Click send or press enter

Additional context Error seems to be from: https://github.com/vercel/next.js/blob/v15.0.2/packages/next/src/server/lib/patch-fetch.ts#L536

The source of the issue appears to be that the cache key computation doesn't account for the fetch body being a Uint8Array: https://github.com/vercel/next.js/blob/v15.0.2/packages/next/src/server/lib/incremental-cache/index.ts#L265

I don't know why Next14 didn't have this issue but 15 does. I thought POST fetches are still supposed to bypass cache completely?

mrthinger commented 2 weeks ago

it looks like they introduced a cache for hot reloads thats on by default.

disabling this fixes the issue.

https://nextjs.org/docs/app/api-reference/next-config-js/serverComponentsHmrCache

timostamm commented 2 weeks ago

Thanks for the detailed report, Evan.

This looks to be a similar issue that Svelte used to have, where universal load functions would fail to serialize pre-fetched requests with a non-JSON request body: https://github.com/sveltejs/kit/issues/8302

From a quick glance, generateCacheKey really doesn't handle Uint8Array at all. It does handle readable streams and blobs (both can hold arbitrary binary data), but interprets them as UTF-8 text, serializes them to a JSON string, then UTF-8 encodes that string to bytes, and passes them to a hash function 🤔

I'm not sure this is intended behavior. It seems reasonable to me to support simple binary request bodies in generateCacheKey. Could you create an issue in the next.js repository?

mrthinger commented 2 weeks ago

Could you create an issue in the next.js repository?

Yes and I appreciate your considerate response. I'll see to it sometime tomorrow.