Search code examples
cachinggraphqlapollo

Apollo server-side caching: What is cache keyed on?


I'm reading the documentation for Apollo server-side caching, but don't see anything that talks about how caching is keyed generally.

What I need is a cache that's keyed on the object IDs contained in a response, not keyed on something that you get from the query.

For example, imagine the below Person object is uniquely identified by the ID field, and the hasShortHair field is expensive to calculate, but changes rarely.

type Person {
  id: String!
  hasShortHair: Boolean!
}

Assume that there are 2 different query types that can return a Person:

getPerson(id: String!): Person!
getAllPeople: [Person!]!

Ideally, for a person with a given ID, if that person has been fetched via either getPerson or getAllPeople recently, then I'd like to cache the value of hasShortHair that was calculated for that person, and use that cache for both getPerson and getAllPeople queries which return that person.

Will setting things up like the following achieve that? (Based on the book example in the documentation)

type Person @key(fields: "id") @cacheControl(maxAge: 30) {
  id: String!
  hasShortHair: Boolean!
}

or would the caching for this still be keyed per-request?


Solution

  • The ApolloServerPluginCacheControl plugin parses cache annotations (e.g. via the @cacheControl directive) for specific fields and types, and based on that it determines an overall cache policy for the particular request.

    Based on the calculated cache policy for the request, it then sets a Cache-Control header on the response object.

    You can review the code for yourself on Github

    That's all it does, there is no cache key involved.

    It's then the responsibility of the client, or some caching proxy in front of Apollo to use the Cache-Control header to do actual caching.

    The header has this format:

    Cache-Control: max-age=60, private
    

    With the max-age determined based on the rules for interpreting all the cacheControl hints.

    If you want to cache the responses with Apollo, you can use the response-cache-plugin.

    More info here: https://www.apollographql.com/docs/apollo-server/performance/caching/#caching-with-responsecacheplugin-advanced

    With the response-cache plugin, you can cache entire responses in, for example, Redis or memcached.

    In this case, the cache key is based on the request itself, i.e. from the plugin docs: cached responses are only used for identical requests.

    So, to answer your question:

    Ideally, for a person with a given ID, if that person has been fetched via > either getPerson or getAllPeople recently, then I'd like to cache the value of hasShortHair that was calculated for that person, and use that cache for both getPerson and getAllPeople queries which return that person.

    The caching is per request/response, and not per individually resolved field.

    From the ApolloServerPluginResponseCache code:

    All writes will be under keys that start with 'fqc:' and are followed by a fixed-size cryptographic hash of a JSON object with keys representing the query document, operation name, variables, and other keys derived from the sessionId and extraCacheKeyData hooks.

    Therefore, if you want to cache individual fields, you'd have to add your own caching mechanism to do that.