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

Upgrading to 0.11 doesn't trigger ApolloLink properly #348

Open
pdubb opened this issue Aug 19, 2024 · 3 comments
Open

Upgrading to 0.11 doesn't trigger ApolloLink properly #348

pdubb opened this issue Aug 19, 2024 · 3 comments

Comments

@pdubb
Copy link

pdubb commented Aug 19, 2024

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
Copy link
Member

phryneas commented Aug 20, 2024

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 #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

export const ApolloNextAppProvider = /*#__PURE__*/ WrapApolloProvider(
buildManualDataTransport({
useInsertHtml() {
const insertHtml = useContext(ServerInsertedHTMLContext);
if (!insertHtml) {
if (process.env.REACT_ENV === "browser") {
//Allow using the browser build of ApolloNextAppProvider outside of Next.js, e.g. for tests.
return () => {};
}
throw new Error(
"The SSR build of ApolloNextAppProvider cannot be used outside of the Next App Router!\n" +
'If you encounter this in a test, make sure that your tests are using the browser build by adding the "browser" import condition to your test setup.'
);
}
return insertHtml;
},
})
);
ApolloNextAppProvider.info = bundle;

And here's the comment on stringifyForStream:

/**
* Prepare data for injecting into the stream by converting it into a string that can be parsed as JavaScript by the browser.
* Could e.g. be `SuperJSON.stringify` or `serialize-javascript`.
* The default implementation act like a JSON.stringify that preserves `undefined`, but not do much on top of that.
*/
stringifyForStream?: (value: any) => string;

@pdubb
Copy link
Author

pdubb commented Aug 20, 2024

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
Copy link
Member

phryneas commented Aug 20, 2024

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",
};

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

2 participants