Apollo client caching "me" field but always going to server

On our graphql server, we have a “me” field. This returns a “User” based on the bearer token in the HTTP headers.

example request:

query GetMe {
    me {
      id
      firstname
      lastname
      ...
    }
  }

The above query doesn’t take any arguments, the server just looks at the bearer token and returns the relevant user.

In apollo-client I’m noticing that it’s caching the result of this field and it knows that it’s a “User” so adds a “User:2” key to the cache but every time I use this query, it’s going to the server.

I’m assuming this is because there’s no key against the “me” field, therefore it doesn’t know that it doesn’t need to go to the server?

My question, is how can I customize the field policy to prevent apollo-client from fetching from the server every time? Or maybe I’m doing this all wrong and there’s a better / more conventional way of providing user information in gql?

Thanks,

Lee

Hello! Your example here appears to be a great way of fetching user information with GraphQL. I’m surprised that this query is using the network every time. If you could provide a snippet of your client code that uses the query within the relevant component, that might help diagnose the issue.

Some initial speculations off the top of my head:

  • You haven’t set a specific fetch policy for the query, correct? (The default fetch policy should skip a network call if the query is cached)
  • You aren’t refreshing the page between query attempts, correct? (The cache does not persist across page loads unless you intentionally persist it)
  • Which version of @apollo/client are you using? I recommend using the latest version (currently 3.5.8) to make sure the docs are in line with your app.

Hi Stephen,

Thanks for the comment!

I’m specifying the query like this:

import {gql} from "@apollo/client";

export const GET_ME = gql`
  query GetMe {
    me {
      id
      ....
    }
  }
`;

and then using the query like this:

import React from "react"';
import {View} from "react-native";
import {useQuery} from "@apollo/client";
import {GetMe} from "graphql/generated";

const MyComponent: React.FC = () => {
    const me = useQuery<GetMe>(GET_ME);

    return (
        <View> 
        ... 
        </View>
    );
}

I was not specifying a fetch policy as I believe that “cache-first” is the default. Since then I have tried setting it explicitly to “cache-first” but to no avail.


  • You aren’t refreshing the page between query attempts, correct? (The cache does not persist across page loads unless you intentionally persist it)

I’m using apollo in react-native, so no page “refreshes”, no. I use this query in the “logged in” navigation stack component so that the data is preloaded. I then use the same query again in other components where I actually need the data.
I’ve wrapped the entire app in an “ApolloProvider” component so the cache should not be affected as I switch from the logged-out to the logged-in navigation stacks.


  • Which version of @apollo/client are you using? I recommend using the latest version (currently 3.5.8 ) to make sure the docs are in line with your app.

I’m currently on 3.5.5 of @apollo/client, but it’s a good shout - I’ll try updating to the latest.


What’s odd is I added a custom field policy to the to InMemoryCache to check that apollo is reading the field from the cache and it does indeed get hit:

export const apolloClient = new ApolloClient({
  uri: API_CONFIG.baseUrl,
  cache: new InMemoryCache({
    typePolicies: {
      Query: {
        fields: {
          ...
          me: {
            keyArgs: false,
            read(existing, args) {
              console.log("reading from cache", existing);

              return existing;
            }
          },
        },
      },
    },
  }),
  link: buildLink(),
});

I see the “reading from cache” in the logs.

I even went on to log “args.cache.extract()” inside of that field policy and I can see “me” and the associated user in the cache:

{
"ROOT_QUERY": {
    "__typename": "Query",
    ...
    "me": {
      "__ref": "User:2"
    }
    ...
  },
}
"User:2": {
    ...
    "__typename": "User",
    "firstname": "Lee",
   ...
  },

This is what the logs look like:

 LOG  reading from cache undefined
 INFO  Operation GetMe took 373ms to complete
 LOG  reading from cache {"__ref": "User:2"}
 LOG  reading from cache {"__ref": "User:2"}
 INFO  Operation GetMe took 65ms to complete
 LOG  reading from cache {"__ref": "User:2"}

So apollo performs the network request the first time, as the cached value is undefined, and caches the value ok. The second time the query is executed, “existing” does have the correct value which is returned from the read function.

It appears that apollo is determining that the returned value is not valid for the query? I’m, wondering if it’s something to do with there being no keyargs for this query?

Thanks again for getting back to me, and apologies for the wall of text!

Lee

Hi @StephenBarlow, sorry to bother you. Do you have any ideas regarding this? Do you think it would be worth logging an issue on the apollo github?

Hello, sorry for failing to respond! Based on your followup info, this behavior does indeed still seem surprising :thinking: posting an issue on the project sounds like a fine idea, and I’ll follow up w/ the core Client team to see if they can spot anything I’m missing.

Thank you so much for the reply! Sorry to have nagged! :slight_smile:

Thanks for giving this a once over - I’ll log an issue on the GitHub. Hopefully it’s something silly that I am doing wrong!

Thanks again!

Lee

Okay, I have some additional hopefully helpful intel!

It sounds likely that you’re roughly simultaneously rendering multiple components that all use this same query. At least, close enough to simultaneously that the first executed query hasn’t completed and updated the cache when the second query kicks off. Therefore, both queries initially see nothing in the cache and must hit the network.

To avoid this, you can have your highest-level component that executes this query wait to render the subcomponents that also use the query by conditionally rendering based on !loading or data from useQuery. In doing so, when the subcomponents do render, the Query.me data will definitely be in the cache.

An upcoming version of Apollo Client will also add support for a new hook called useFragment, which will provide the subcomponents with a cleaner API for relying solely on cached data. I’ll try to remember to update this thread when that version is released :smile:

Hi Stephen,

Thank you so much for this.

I too thought that it might be a case that if the second query starts before the first finishes (and updates the cache with its data) this would occur, so I moved the second useQuery to a new screen in the stack navigator.

I’m using the roundTrip link example in the apollo docs to prove when a network request is being made. The second network request doesn’t occur until I navigate to a new screen. proving that it’s not a race condition and that a separate call is hitting the cache but still fetching from the network following that.

I feel like it might have something to do with the “me” field just being a ref to a user in the cache, but I haven’t been able to prove that yet. It’s getting a bit late here so I’m going to try to put together a reproducible example tomorrow.

I really appreciate your time and help on this.

Thanks again,

Lee