Apollo-client: fetchMore/keyArgs not working as expected (duplicate queries and no concatenation)

Created on 11 Nov 2020  路  5Comments  路  Source: apollographql/apollo-client

Intended outcome:
Calling fetchMore on a paginated query concatenates results to existing query.

Actual outcome:
I see 2 requests in the network tab, one with the correct offset value, another with the original value. Results of the fetchMore query are not concatenated to the original list.

How to reproduce the issue:
I have a schema that looks like this:

input PostFindArgs {
   ...
   order: String
   offset: Int
}

type Post {
   id: Int
   text: String
   ...
}

type Query {
   posts(params: PostFindArgs): [Post]
}

Cache configuration like this:

const client = new ApolloClient({
  link: ApolloLink.from([authLink, errorLink, httpLink]),
  cache: new InMemoryCache({
    typePolicies: {
      Query: {
        fields: {
          posts: {
            // keyArgs: ['params.order'],
            merge(existing = [], incoming) {
              return [...existing, ...incoming];
            },
          }, 
         //concatPagination(['params']),
        },
      },
    },
  }),

And usage like this:

const { data, refetch, loading, fetchMore } = useQuery(POSTS_QUERY, {
    variables: { params: { order: 'newest', offset: 0, ... } },
});

// button calls this
fetchMore({variables: {params: {offset: 10}}});

I didn't see anything in the documentation about how to handle keyArgs if the query arguments are an object. I would like to do something like keyArgs: ['params.order'], but that throws an error. I imagine the problem here is that without keyArgs, the object containing {offset: 10} resolves in the cache as a separate query, so it doesn't concatenate results to the existing query. I also tried using the concatPagination helper, with no luck.

However, while this might explain the missing concatenated results, it doesn't explain why I see 2 fetches in the network tab. A console.log shows that the fetchMore function is only called once, but I still see 2 network requests, one with the correct offset, and a second with the original offset.

Does Apollo Client v3 support object-based keyArgs? If not, should I go back to using updateQuery?

Versions
System:
OS: macOS 10.15.7
Binaries:
Node: 14.15.0 - ~/.nvm/versions/node/v14.15.0/bin/node
Yarn: 1.13.0 - /usr/local/bin/yarn
npm: 6.14.8 - ~/.nvm/versions/node/v14.15.0/bin/npm
Browsers:
Chrome: 86.0.4240.198
Edge: 86.0.622.63
Firefox: 66.0.3
Safari: 14.0
npmPackages:
@apollo/client: ^3.2.5 => 3.2.5
apollo-upload-client: ^14.1.3 => 14.1.3

Most helpful comment

@CaptainStiggz We do support nested objects for both keyArgs and keyFields, though the syntax is a little unusual: keyArgs: ["params", ["order"]] gives you a field key like posts({"params":{"order":...}}), and keyArgs: ["params", ["order", "offset"]] gives a field key like posts({"params":{"order":...,"offset":...}}). The benefit of this array-based syntax is that it fully specifies the order of the keys, so you don't have to worry about non-deterministic JSON.stringify serialization.

All 5 comments

Hi, I already posted an issue with the "double query" #7307 (which happens to me only with fetchPolicy set with 'network-only' or 'cache-and-network').

However, I believe that "keyArgs" takes an array of the "variables" object keys. Since your call is structured like this variables: { order: 'newest', offset: 0, ... }, keyArgs: ['order'] should suffice. Because it depends on the variables you always can "split" your args in your query.

A more detailed fetchMore example there : #7302

@jgan42 whoops, I had an error the way I typed out my example. Thanks for catching that.

variables: { params: { order: 'newest', offset: 0, ... } }
(Note, variables: { params: { ...} } instead of variables: { ... }.)

I found your issue earlier, but unfortunately the const [offset, setOffset] = useState(0), with fetchMore(...).then(() => setOffset(o => o + res.data.posts.length)) trick did not work for me. If I do that, I instead see the query fetched THREE times instead of two. First, with the correct offset, then with the original offset, then a third time with the correct offset again.

I suspect it has something to do with my query variables encapsulated in an object. I.E. The type is
{variables: {params: PostFindArgs} }

@CaptainStiggz We do support nested objects for both keyArgs and keyFields, though the syntax is a little unusual: keyArgs: ["params", ["order"]] gives you a field key like posts({"params":{"order":...}}), and keyArgs: ["params", ["order", "offset"]] gives a field key like posts({"params":{"order":...,"offset":...}}). The benefit of this array-based syntax is that it fully specifies the order of the keys, so you don't have to worry about non-deterministic JSON.stringify serialization.

@benjamn , could we add that syntax for nested args to the docs? I spent an hour this morning trying to figure that out when I came across this issue. Would be super useful as a callout/example.

Just spent two hours to get how to do the nested argument stuff. Please, please add this to the documentation !

Was this page helpful?
0 / 5 - 0 ratings