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

Apollo Client omits fields from fragments on an interfaces #11624

Closed
maslade opened this issue Feb 26, 2024 · 7 comments
Closed

Apollo Client omits fields from fragments on an interfaces #11624

maslade opened this issue Feb 26, 2024 · 7 comments

Comments

@maslade
Copy link

maslade commented Feb 26, 2024

Issue Description

Apollo Client strips fields from results when those fields were queried by way of a fragment on an interface. That's a mouthful, but the repro steps are simple and show it clearly. Note that this does not align with the behavior of apollo-server, which performs as I expect and returns everything for which I query.

I can work around this in two ways:

  • Use one fragment per subtype, each including the same fields from the common interface
  • Don't use fragments. If I query directly for interface-level fields, they are returned without issue

Related: #8898. I chose not to add to that ticket, as @phryneas already noted that it had become unclear, whereas this is a specific and reproducible bug.

Link to Reproduction

https://github.com/maslade/apollo-client-bug-reproduction

Reproduction Steps

  1. Run npm install && npm start
  2. Load the web page in your browser. It will run at http://localhost:8080 by default.

Expected behavior

I see one item of type ExampleInterface, including its typename, ID, and name. When I immediately
log the results of useQuery, they match what I see in the network tab. It looks like this:

{
  "__typename": "ExampleTypeB",
  "id": "1",
  "name": "Example Name"
}

Actual behavior

I see one item with only a __typename field.

Background

This occurs when querying for an interface using a fragment.

I can work around it by not (A) using fragments or (B) using a fragment for each subtype. Both
options are problematic.

Workaround A

  const ExampleQuery = gql`
    query {
      exampleQuery {
        __typename
        id
        name
      }
    }
  `;

Workaround B

  const ExampleQuery = gql`
    fragment ExampleFragmentA on ExampleTypeA {
      __typename
      id
      name
    }

    fragment ExampleFragmentB on ExampleTypeB {
      __typename
      id
      name
    }

    query {
      exampleQuery {
        __typename
        ...ExampleFragmentA
        ...ExampleFragmentB
      }
    }
  `;

@apollo/client version

3.9.5

@phryneas
Copy link
Member

Hi Mark,

I'm looking at the reproduction and it doesn't seem to reproduce what you are describing here - could it be that you forgot pushing the final version of the reproduction to the repo?

Right now, there are no fragments at all in that reproduction.

That said, the behaviour you describe here roughly sounds like you might be missing possibleTypes.
InMemoryClient needs that configuration option to be aware which types implement which interfaces, and you don't seem to be providing that.

@maslade
Copy link
Author

maslade commented Feb 27, 2024

@phryneas I accidentally committed my work-around -- whoops. I pushed the update. I took your advice and providing possibleTypes corrects the behavior.

It seems like strange behavior given the information available to the client:

  1. I asked for these fields
  2. The server returned these fields

Can the client detect cases where it has insufficient information to complete a query, and issue warnings to the developer?

@phryneas
Copy link
Member

Unfortunately, there are more complex cases where it would quickly become unpredictable if we were to just take the server answer.

You could have something like

  const ExampleQuery = gql`
    fragment ExampleFragmentA on InterfaceA {
      __typename
      id
      name: fieldA
    }

    fragment ExampleFragmentB on InterfaceB {
      __typename
      id
      name: fieldB
    }

    query {
      exampleQuery {
        __typename
        ...ExampleFragmentA
        ...ExampleFragmentB
      }
    }
  `;

The cache would need to save this in a normalized fashion.
Now you get something with a name back from the server.
Do you save it in the cache as fieldA or as fieldB?

=> We need the information which types implement which interfaces, or otherwise we'd end up with unpredictable behaviour.

And since the Client has no knowledge of the Schema (it could be multiple megabytes big!), you have to tell it about those connections with possibleTypes.

I believe this should already trigger a warning. Did you increase the verbosity?

import { setVerbosity } from "ts-invariant";
setVerbosity("debug");

@maslade
Copy link
Author

maslade commented Feb 27, 2024

That makes sense. Thank you for walking me through the problem.

I did test setVerbosity("debug") and don't see anything in the logs. I pushed this to my repro. Am I doing something wrong?

@maslade
Copy link
Author

maslade commented Feb 28, 2024

Following up to say that this ticket can be closed. I'd love to see the client more proactively nudge developers impacted by this, but the problem and solution are covered by existing docs.

@maslade maslade closed this as completed Feb 28, 2024
Copy link
Contributor

Do you have any feedback for the maintainers? Please tell us by taking a one-minute survey. Your responses will help us understand Apollo Client usage and allow us to serve you better.

Copy link
Contributor

This issue has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.
For general questions, we recommend using StackOverflow or our discord server.

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Mar 30, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

3 participants