Apollo GraphQL client: how to distinguish an optimistic response from a real response into a watchQuery
Asked Answered
D

2

9

The question is about the interaction of a mutation, optimistic response, and a watchQuery.

I have a mutation "myMutation" which has an "optimisticResponse" and an implemented "update" function.

Every time I do a mutation query the "update" function is called twice, the first time with optimistic response data and the second one with real data. All is Ok and all as described in the documentation.

Into my "update" function I modify "myQuery" cache data through using readQuery/writeQuery methods.

Every time I modify "myQuery" cache data a watchQuery (based on "myQuery") subscription is called. All is Ok and all as described in the documentation.

But the problem is that I cannot distinguish into my watchQuery whether I receive optimistic response data or real response data. It is crucial for me because the reaction must be different since valuable part of data can be provided by a server only. I should show a GUI element with a special style when I receive an optimistic response and I should prohibit any interactions with it until I receive a real response.

Unfortunately, I can't solve this matter. At a glance, there is no difference between optimistic and real responses. I've googled a lot and haven't found a solution. The only idea I have is adding a special field to my GraphQL data which will show whether a response is received from a server or not. But it looks ugly and smells bad. I am sure, there must be a simple correct way to overcome the problem.

Dominique answered 16/5, 2018 at 7:44 Comment(0)
N
6

Maybe there is an easier way or there will be one in the future but here is what I know.

The data in optimisticResponse is only provided during the first call to update. That is where you can flag to your update function that it is dealing with optimistic data. You can put any data you want there. I put isOptimistic: true,.

To deal with the watchQuery issue, I recommend you make use of apollo-link-state to add a client-only field or fields to the areas of your data model where optimistic upserts should be known to the display. Don't include isOptimistic in your mutation query so you know it's from the server and not the optimistic response and force it to false if it's not true. See this example:

const SUBMIT_COMMENT_MUTATION = gql`
  mutation submitComment($repoFullName: String!, $commentContent: String!) {
    submitComment(repoFullName: $repoFullName, commentContent: $commentContent) {
      postedBy {
        login
        html_url
      }
      createdAt
      content
    }
  }
`;

const CommentsPageWithMutations = ({ currentUser }) => (
  <Mutation mutation={SUBMIT_COMMENT_MUTATION}>
    {(mutate) => (
      <CommentsPage
        submit={(repoFullName, commentContent) =>
          mutate({
            variables: { repoFullName, commentContent },
            optimisticResponse: {
              __typename: 'Mutation',
              submitComment: {
                __typename: 'Comment',
                postedBy: currentUser,
                createdAt: new Date(),
                content: commentContent,
                isOptimistic: true, // Only provided to update on the optimistic call
              },
            },
            update: (proxy, { data: { submitComment } }) => {
              // Make sure CommentAppQuery includes isOptimistic for each comment added by apollo-link-state
              // submitComment.isOptimistic will be undefined here if it's from the server
              const newComment = {
                ...submitComment,
                isOptimistic: submitCommit.isOptimistic ? true : false,
              };

              // Read the data from our cache for this query.
              const data = proxy.readQuery({ query: CommentAppQuery });

              // Add our comment from the mutation to the end.
              data.comments.push(newComment);

              // Write our data back to the cache.
              proxy.writeQuery({ query: CommentAppQuery, data });
            },
          })
        }
      />
    )}
  </Mutation>
);

See https://www.apollographql.com/docs/link/links/state.html.

Nedrud answered 16/5, 2018 at 14:12 Comment(5)
Thank you very much! It is exactly what I need.Dominique
Unfortunately, another problem occurred. Then I use apollo-link-state mutation and watchQuery work properly. But if a client-only field exists also in a subscription it suddenly stops after the first response is received. I use a default: Sorry, I do not understand why a code block doesn't work. <code> const defaultState = { _loadingStatus: 'READY' }; const stateLink = withClientState({ cache, defaults: defaultState, resolvers: {} }); </code> which I change in my optimistic response.Dominique
@Dominique There's a lot going on with your issue I cannot see. However, you said it stops working after the client-only field is added to the subscription. Could you try to not have it in the subscription and then just read that value from cache within your subscribe logic? It may be a bug in the subscription logic with link-state. I don't know.Nedrud
Thank you for your response. It looks like a bug in apollo-link-state. I created an issue on this matter: github.com/apollographql/apollo-link-state/issues/249Dominique
To make this solution work with Apollo 3 see the answer below by Francois NadeauHargreaves
G
1

I couldn't get this to work on Apollo 3.X by only adding a property on the optimistic response, the property was getting stripped away. To get it to work I had to add a local variable to the query.

fragment object on Object {
    id
    isOptimistic @client
...

Once that is done, I was able to add the local-only flag to my optimistic response.

const optimisticResponse = {
   object: {
    id: "temp-id",
    isOptimistic: true,
    ...
   }
}
Gretagretal answered 1/6, 2022 at 12:45 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.