apollographql / apollo-client

:rocket:  A fully-featured, production ready caching GraphQL client for every UI framework and GraphQL server.
https://apollographql.com/client
MIT License
19.38k stars 2.66k forks source link

Unsure how to resolve `Missing field while writing result` error when using subscriptions #8677

Open blehoux17 opened 3 years ago

blehoux17 commented 3 years ago

We've implement subscriptions in our application using the useSubscription hook and since upgrading from version 3.3.21 to 3.4.7 we've started to see errors around Missing field.... To be clear we realize these errors have likely always existed, but now they're being surfaced.

We see the following error Missing field 'domainObject' while writing result {} on initial page load when the subscription is registered. We believe that this happens because the first response from the subscription is an empty object. The subscriptions themselves work fine, this only seems to be an issue when they're initially registered. We'd like to resolve this issue and remove the errors from the console, but have so far been unsuccessful.

We tried to update the fetchPolicy and found that setting it to no-cache removed the error, but this stopped the cache from being updated at all, which defeats the purpose.

Shown below is a snippet of the code that we've implemented. Some of the subscriptions allow apollo to do the cache updating on its own (OnObjectUpdated) while with others (OnObjectDeleted) we capture the return data and manually make a change to the cache. In both cases we see the Missing field error. For the OnObjectDeleted subscription, we added a guard in our callback function because when the subscription is registered we receive data as an empty object.

subscription OnObjectDeleted {
  objectDeleted {
    object {
      id
    }
  }
}
const removeObjectFromCache = ({ subscriptionData: { data } }) => {
  if (!data || !data.objectDeleted) return;

  removeObject(data.objectDeleted.object.id, client);
};

useSubscription(OnObjectDeleted, { onSubscriptionData: removeObjectFromCache });
useSubscription(OnObjectUpdated);

We're looking to see if anyone has encountered this and if so how it was resolved.

Thanks!

ragafus commented 2 years ago

I'm still facing this issue with "@apollo/client": "3.7.0", using useQuery with relay style pagination. It's a bit scary how an undefined value can break the cache and the pagination.

Given these two types

type MainSkills =  {
  hard?: string;
  soft?: string;
}

type User = {
  id
  name
  skills?: MainSkills
}

I use two fragments for the type User

fragment UserBasicData {
  id
  name
}

fragment UserFullData {
  id
  name
  skills {
    hard
    soft
  }
}

The errors appears when the data from the fragment UserBasicData is already stored in the cache and the data is refreshed with the data from UserFullData.

vxm5091 commented 2 years ago

+1 on "@apollo/client": "3.6.8"

Same scenario as @ragafus

ryancrunchi commented 2 years ago

+1 with 3.7.0 Also having issue with subscription creating cache data for a different entity if the typename does not match the one of initial query. For example, query typename is User subscription typename is UserUpdate apollo creates a cache entry for UserUpdate, even with fetch policy no-cache set on subscription even if onData calls a cache.updateQuery on initial query

medemi68 commented 2 years ago

How has nobody from Apollo commented on this post. Am I missing something?

medemi68 commented 2 years ago
  updateQuery: (prev, { subscriptionData }) => {
        if (!subscriptionData.data) return prev;

        const newFeedItem = subscriptionData.data.sortedMessageSubscription;

        const newMessagesCached = {
          sortedMessages: prev.sortedMessages.concat(newFeedItem),
        };

        return Object.assign({}, prev, newMessagesCached);
      },

I solved mine by changing the code as above... where sortedMessages is the name of the field saved in the cache I really hope this helps someone

This solved the problem for me.

Abdelmonimsamadi commented 1 year ago

i have the same issue here

bxter commented 1 year ago

me too, any one can help this?

image
olegchursin commented 1 year ago

Same here. apollo-angular v3.0.0

PanKarton commented 1 year ago

Menaged to fix it somehow. I had this bug in that function

fetchMore({
      // Update page variable based on
      variables: {
        page: endCursor,
        pageSize: 1,
      },
      // Merge old data with new data and return(update) current data
      updateQuery: (prevResult, { fetchMoreResult }) => {
        if (!fetchMoreResult) return prevResult;
        const prevData = prevResult.newsPosts.data;
        const newData = fetchMoreResult.newsPosts.data;
        const mergedData = [...prevData, ...newData];

        return {
          newsPosts: { data: mergedData },
        };
      },
    });

Problem was that i returned only mergedData instead of updating just part of the object where my posts were stored, so I was overriding whole data object and compiler couldn't find newsPosts in data object, because my data object was actually mergedData array. Maybe it will help sameone idk xD

apss-pohl commented 1 year ago

I am running into this issue because i have to use an await within "updateQuery". As soon as i set "updateQuery" async the issue pop`s up. I could not find any guidance on that. Cant i use async at all in this context?

rebz commented 1 year ago

https://github.com/apollographql/apollo-client/issues/8677#issuecomment-1011921151

I too am seeing this error surface after upgrading this morning...

Original versions.

    "@apollo/client": "^3.2.1",
    "@vue/apollo-composable": "^4.0.0-alpha.14",

Upgraded versions.

    "@apollo/client": "^3.4.13",
    "@vue/apollo-composable": "^4.0.0-alpha.15",

Not sure if it's the best solution but this eliminated the error:

error is: ExceptionsManager.js:184 Missing field 'chat_channel' while writing result {}

updateQuery: (prev, { subscriptionData }) => {
        if (Object.keys(prev || {}).length === 0) {
          // there's nothing in cache, return empty array otherwise crash!
          return { chat_channel: [] }; <---------- instead of return prev;
        }

Solved my issue. Didn't realize it was due to a Subscription lacking the a field the query originally contained. I tweaked the return data on updateQuery to combine the incoming subscription data with the previously known data. So now I can maintain the missing field data while getting updates on the data I care about.

jnsandrew commented 1 year ago

I had this error appear using MockedProvider / storybook-addon-apollo-client while mocking a specific query. It was complaining about Missing field icon.... In my case the mocked data being returned was in the wrong format:

Query:

export default gql`
  query GetIconQuery($iconId: String!) {
    icon(iconId: $iconId) {
      url
    }
  }
`;

Mocked data that was erroring:

{
   request: {
     query: GetIconQuery,
      variables: {
        iconId: icon.id,
      },
    },
    result: {
     data: {
       url: `icon.svg`,
     },
   },
}

The fix is to change result.data.url with result.data.icon.url because that's the name of the query

{
   request: {
     query: GetIconQuery,
      variables: {
        iconId: icon.id,
      },
    },
    result: {
     data: {
       icon: {
         url: `icon.svg`,
       }
     },
   },
}
alessbell commented 1 year ago

Hi everyone 👋 Can anyone share a runnable reproduction here? With the number of comments—many of which seem to have been resolved by ensuring the data returned matches the shape of the initial request—it's difficult to determine whether there is anything actionable on our end.

We have a CodeSandbox and forkable GitHub repo with a subscriptions.tsx already configured. Thanks!

at2706 commented 1 year ago

Hello. I'm getting this error when mocking query results. Here's a CodeSandbox example: https://codesandbox.io/s/apollo-client-missing-field-error-srr15m

bcgilliom commented 1 year ago

@alessbell adding every field does solve the issue, but it would be super nice to be able to suppress that warning in tests so nullable fields we don't care about for a test don't need to be specified

hshoja commented 1 year ago

fixed the error by adding fetchPolicy: "no-cache" in useSubscription

"@apollo/client": "3.7.16",
"@rails/actioncable": "7.0.6",
 "graphql-ruby-client": "1.11.8",
jcpeden commented 1 year ago

Thanks for sharing @hshoja, this fixed the issue for me when testing with React Testing Library and

AndreiRailean commented 7 months ago

We faced a similar issue and upon close investigation, we ended up blaming it on ActionCable and how graphql-ruby gem works with it. The main issue is that upon initial subscription, it returns data={} because that's how empty result gets serialised by .to_h call in ruby, i.e. it when result.data=nil, and the code runs result.to_h, you end up with result: data: {}.

This appears to have been a deliberate change introduced to better support Relay in 2019 in https://github.com/rmosolgo/graphql-ruby/pull/2536

https://github.com/rmosolgo/graphql-ruby/pull/2536/files#diff-a1ea5af1a78917fa653eff83d9ae72180fabd3d9cf616f14dd1871739963cd73L44

We ended up changing our implementation on the ruby side, which ended up setting data: null. This stops Apollo client from erroring because of type mismatch. We didn't want to manually introduce a type override for each subscription we add. This approach allows us to support initial subscription result if we ever wanted to.

image

And we went from this in our cable stream

image

To this

image

And there were no more errors and all subscriptions kept on working as expected.

If you're running into this error, it may help to consider adjusting the server, rather than waiting for Apollo client to ever change to support this. The issue is really with what restrictions ActionCable places on graphql-ruby for delivering subscriptions. By default, graphql-ruby is conffigured to NOT send a payload for initial subscription, but when it comes to ActionCable channels, it appears unavoidable that something will be sent and in this case it ends up being formatted incorrectly.

cc @rmosolgo

rikas commented 7 months ago

@AndreiRailean I had exactly the same issue but I didn't have to fiddle around with graphql-ruby internals.

When I left the default subscribe method in one of my subscriptions I was getting the error described in this thread (because of data = {}) but then I tried to just override the subscribe to return nil and apparently that worked.

class MySubscription < BaseSubscription
  argument :whatever_argument, String, required: true

  def subscribe(whatever_argument:)
    nil
  end
end

This is now sending data = null in the cable connection.

Hope that helps people using graphql-ruby.

melissap-nulogy commented 4 months ago

@AndreiRailean I had exactly the same issue but I didn't have to fiddle around with graphql-ruby internals.

When I left the default subscribe method in one of my subscriptions I was getting the error described in this thread (because of data = {}) but then I tried to just override the subscribe to return nil and apparently that worked.

class MySubscription < BaseSubscription
  argument :whatever_argument, String, required: true

  def subscribe(whatever_argument:)
    nil
  end
end

This is now sending data = null in the cable connection.

Hope that helps people using graphql-ruby.

When I do this I get Cannot return null for non-nullable field Subscription.objectDeleted and the subscriptions stop working.

tintin10q commented 3 months ago

Can someone close this issue? It is not about the same thing anymore as the initial issue.