Using fragments in combination with `PreloadQuery` leads to `unique-names` warnings
What's the matter?
When using the PreloadQuery component with a query that contains fragments, we get following unique-names warning in the console:
Warning: fragment with name Compoany already exists.
graphql-tag enforces all fragment names across your application to be unique; read more about
this in the docs: http://dev.apollodata.com/core/fragments.html#unique-names
What do we expect?
Using PreloadQuery should not lead to unique-names warnings.
Also: The links is not very helpful because the page doesn't exist anymore.
How to reproduce?
Take a look into the console of following example https://stackblitz.com/edit/nextjs-869gd8?file=app%2Fexample-query.ts,app%2Fpage.tsx
This is a weird one, and it seems to be a quirk of how React implements Client Component imports.
I'll try to break it down, but this graphic might also help:
So, what happens here is that our RSC component imports from queryFile.js. That executes gql'interface MyInterface ... for the first time.
It also imports from clientFile.jsx. Now, nothing imported from a Client File is actually imported into the RSC context. So it doesn't really import the function MyUserComponent, but a "client reference" - an object that describes that this is a reference into a client file, to be actually imported and used during a Client Component render.
At the same time, though, it needs to know if that export exists and what that export actually is (is it a Component or something else you couldn't even reference in RSC?).
For that, clientFile.jsx needs to be executed. And clientFile.jsx also imports from queryFile.js. Mind that now we are in a "client import context" though, so this queryFile.js is now treated as a different file than the original queryFile.js - it needs to be executed again to find out what's exported from there.
Unfortunately, that executes the gql call again, and that triggers the graphql-tag warning that you are parsing a fragment of a name that's already known.
I'll have to dig deeper to find a solution for this, and I guess I'll have to make some changes to graphql-tag to that. (I'll use that as an opportunity to update those urls as well, and give the package some maintenance.)
I can't give you a definite timeline on that - we're about to release Apollo Client 3.11 RC next week, so that has priority for now. I hope I can get to it once 3.11 has been released.
In the meantime, you can disable that warning with
import { disableFragmentWarnings } from 'graphql-tag';
disableFragmentWarnings()
PS: Gruesse nach Hamburg :)
@phryneas thanks a lot for the detailed explanation und liebe Grüße zurück.
@phryneas Thanks for the details. Do you think this could also lead to refetchQueries not working properly? I suspect this is the culprit for my issues.
@Algram I would not expect so, but if you can create a reproduction, please open a new issue and I'll take a look.
I think this is an issue with useSuspenseQuery as well. In this case, the query is executed on the server side, and the query and data are written into the HTML response in manual-transport.ssr.js:
// src/ManualDataTransport/dataTransport.ts
function transportDataToJS(data, stringify2) {
const key = Symbol.keyFor(ApolloSSRDataTransport);
return `(window[Symbol.for("${key}")] ??= []).push(${htmlEscapeJsonString(
stringify2(data)
)})`;
}
On the client side, the query is parse-stringify-parsed in @apollo/client-react-streaming:
function deserializeOptions(options) {
return {
...options,
// `gql` memoizes results, but based on the input string.
// We parse-stringify-parse here to ensure that our minified query
// has the best chance of being the referential same query as the one used in
// client-side code.
query: gql(print(gql(options.query)))
};
}
The query that is written to the HTML response has newlines removed, but the query after being parse-stringified has the newlines present. This causes gql to consider it a different query, which results in the duplicate fragment warning.
Perhaps remove newlines when memoizing the results of parsing the query?
Edit: a bit more digging reveals that the newlines are actually removed in @apollo/client-react-streaming, index.ssr.js:
// src/DataTransportAbstraction/transportedOptions.ts
function serializeOptions(options) {
return {
...options,
query: printMinified(options.query)
};
}
Thanks for bringing that up, that's another good point.
Even if it were 100% the same, you'd get that warning... The graphql-tag package was just made years before these patterns even existed.
Like I said, I'll have to do some work there, but I can't give you a timeline - we have quite a lot of things to take care of. It's definitely on the list!
For now, please disable the warnings.
@phryneas We are using gql.tada and not graphql-tag. How would we go about disabling the warnings in that case?
@Algram I'll have to look into that. Could you please give me the full text of the warning?
@phryneas Sure:
[warn] Warning: fragment with name PageInfo already exists.
graphql-tag enforces all fragment names across your application to be unique; read more about
this in the docs: http://dev.apollodata.com/core/fragments.html#unique-names
@Algram That error message seems to come from graphql-tag, not from gql.tada. Have you tried the method I've outlined above?
I have tried it and it doesn't work. graphql-tag is coming as a dependency of apollo and produces the error. We are not using graphql-tag for defining our fragments though, but rather gql.tada. I guess the disableFragmentWarnings function does not work in such a setup (?)
Can you try
import { disableFragmentWarnings } from '@apollo/client';
disableFragmentWarnings()
instead?
@phryneas we're getting the same fragment unique-names warning, we tried to disable it by importing (and calling) disableFragmentWarnings from either graphql-tag or @apollo/client, without effect so far.
@nduchon did you make sure to do that call in the client bundle, meaning in a file with "use client"?
Hi,
We have the same problem here using useBackgroundQuery and useReadyQuery. fragments are defined in only one place yet we have duplicate fragment warnings on page load (Next.js).
Suppressing the warnings clears these from the console, though. Thanks for that suggestion!
Anything I can provide that might help with finding a fix?
disableFragmentWarnings()
That works, thank you. Any permanent solution ahead?