Search code examples
apollographql-jsapollo-client

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


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.


Solution

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