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

Using fragments in combination with PreloadQuery leads to unique-names warnings #328

Open
dlehmhus opened this issue Jul 3, 2024 · 15 comments

Comments

@dlehmhus
Copy link

dlehmhus commented Jul 3, 2024

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

@phryneas
Copy link
Member

phryneas commented Jul 4, 2024

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:

image

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 :)

@dlehmhus
Copy link
Author

dlehmhus commented Jul 4, 2024

@phryneas thanks a lot for the detailed explanation und liebe Grüße zurück.

@Algram
Copy link

Algram commented Jul 22, 2024

@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.

@phryneas
Copy link
Member

@Algram I would not expect so, but if you can create a reproduction, please open a new issue and I'll take a look.

@joaquimds
Copy link

joaquimds commented Jul 27, 2024

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)
  };
}

@phryneas
Copy link
Member

phryneas commented Jul 29, 2024

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.

@Algram
Copy link

Algram commented Jul 31, 2024

@phryneas We are using gql.tada and not graphql-tag. How would we go about disabling the warnings in that case?

@phryneas
Copy link
Member

@Algram I'll have to look into that. Could you please give me the full text of the warning?

@Algram
Copy link

Algram commented Jul 31, 2024

@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

@phryneas
Copy link
Member

@Algram That error message seems to come from graphql-tag, not from gql.tada. Have you tried the method I've outlined above?

@Algram
Copy link

Algram commented Jul 31, 2024

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 (?)

@phryneas
Copy link
Member

Can you try

import { disableFragmentWarnings } from '@apollo/client';

disableFragmentWarnings()

instead?

@nduchon
Copy link

nduchon commented Sep 25, 2024

@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.

@phryneas
Copy link
Member

phryneas commented Oct 7, 2024

@nduchon did you make sure to do that call in the client bundle, meaning in a file with "use client"?

@rob-strover-axomic
Copy link

rob-strover-axomic commented Dec 13, 2024

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?

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

6 participants