apollographql / apollo-client-nextjs

Apollo Client support for the Next.js App Router
https://www.npmjs.com/package/@apollo/experimental-nextjs-app-support
MIT License
449 stars 35 forks source link

Upgrading to 0.11 doesn't trigger ApolloLink properly #348

Open pdubb opened 2 months ago

pdubb commented 2 months ago

We're attempting to upgrade @apollo/experimental-nextjs-app-support and running into issues. It appears we can upgrade @apollo/client to 3.11.4 without any problems, and upgrading @apollo/experimental-nextjs-app-support to 0.10.1 still works. But, upgrading to 0.11.x is resulting in a custom ApolloLink from running properly.

We are using apollo-link-scalars to transform specific graphql fields before they get cached in the apollo client. https://github.com/eturino/apollo-link-scalars

Example:

export const TYPES_MAP: FunctionsMap = {
  ISO8601Date: {
    serialize: (parsed: unknown): string => {
      if (parsed instanceof Date) {
        return formatISO(parsed, { representation: "date" });
      }
      throw new GraphQLError(`Cannot serialize into ISO date: ${parsed}`);
    },
    parseValue: (raw: unknown): Date | null | undefined => {
      if (raw === null || raw === undefined) return raw;
      if (typeof raw === "string") {
        return parseISO(raw);
      }
      throw new GraphQLError(`Cannot parse ISO date: ${raw}`);
    },
  },
};

const scalarsLink = withScalars({ schema: buildSchema(SCHEMA), typesMap: TYPES_MAP });

return new NextSSRApolloClient({
  cache: new NextSSRInMemoryCache({ ... }),
  link: ApolloLink.from([scalarsLink, httpLink]),
});

Before version 0.11, SSR would convert a string date (e.g. "2024-08-19") into a JavaScript Date object, and then the result would be serialized for the client. Then the client would read the query from the cache and have the JavaScript Date object available in the data returned.

However, after upgrading to 0.11, SSR still converts the string date into a JavaScript Date object and serializes the query results as it always has, but after the client hydrates, the date is not converted back to a Date object and only exists as a string (e.g. "2024-08-19T00:00:00Z") from the SSR serialization.

phryneas commented 2 months ago

Yes, with 0.11.0 we dropped the superjson dependency as that caused ESM/CJS problems for a lot of our users and bloated the size of transported data quite a bit - for most people without a good reason, as without extra userland code GraphQL results are just plain objects.

See the https://github.com/apollographql/apollo-client-nextjs/pull/274.

What that means, though, is that Date instances will just be JSON.stringifyd, which cannot be undone at revival time.

You could get around that by creating your own ApolloNextAppProvider and passing in e.g. the serialize-javascript as stringify function into stringifyForStream.

For our implementation, see

https://github.com/apollographql/apollo-client-nextjs/blob/da4c6f8705bd78a789073521cda81c1a8e5afe01/packages/experimental-nextjs-app-support/src/ApolloNextAppProvider.ts#L48-L66

And here's the comment on stringifyForStream:

https://github.com/apollographql/apollo-client-nextjs/blob/cee4e6639c06f4efc35e485f88e91759da4bd8e9/packages/client-react-streaming/src/ManualDataTransport/ManualDataTransport.tsx#L16-L21

pdubb commented 2 months ago

Thank you. That seems to work. Though I was getting the following error:

When using `ApolloClient` in streaming SSR, you must use the `ApolloClient` export provided by `"@apollo/client-react-streaming"`

So, I changed the import of ApolloClient and InMemoryCache to come from @apollo/client-react-streaming instead of @apollo/experimental-nextjs-app-support. Is there any reason this can cause problems or should it be safe moving forward?

phryneas commented 2 months ago

At this point, those are equal and it should not be a problem. We just have that warning in place in case those packages ever deviate from each other. Right now we mostly want to prevent that you use the ones from @apollo/client.

If you want to keep the ones from @apollo/experimental-nextjs-app-support, make sure to also do that

ApolloNextAppProvider.info = bundle;

in the last line of the snipped above, which updates that package name information.

bundle in this case is

{
  pkg: "@apollo/experimental-nextjs-app-support",
  client: "ApolloClient",
  cache: "InMemoryCache",
};