Home >Web Front-end >CSS Tutorial >Working With GraphQL Caching
GraphQL cache mechanism analysis: breaking cache errors
You may have heard of things like "GraphQL does not support caching" or "GraphQL does not care about caching". This is a big problem for many people. But that's not the case. GraphQL's official document mentions a variety of caching technologies, which shows that its development team attaches great importance to caching and its performance advantages.
This article aims to clarify the relationship between GraphQL and cache, and introduce different caching techniques and how to optimize GraphQL queries using cache.
Consider the following query to get a post and its author:
query getPost { post(slug: "working-with-graphql-caching") { id title author { id name avatar } } }
The "secret" to implement GraphQL automatic caching is the __typename
meta field, which is provided by all GraphQL APIs. As the name implies, __typename
returns the type name of the object. This field can even be added manually to an existing query, which most GraphQL clients or CDNs will automatically add, such as urql. The query received by the server may look like this:
query getPost { post(slug: "working-with-graphql-caching") { __typename id title author { __typename id name } } }
The response containing __typename
might look like this:
{ data: { __typename: "Post", id: 5, title: "Working with GraphQL Caching", author: { __typename: "User", id: 1, name: "Jamie Barton" } } }
__typename
is the key to GraphQL cache, because it allows us to cache the results and know that it contains Post ID 5 and User ID 1.
Library such as Apollo and Relay also provide a certain level of built-in caching for automatic caching. Since they already know what is in the cache, they can leverage the cache instead of the remote API to get what the client requests in the query.
Suppose the post author uses editPost
variant to modify the post's title:
mutation { editPost(input: { id: 5, title: "Working with GraphQL Caching" }) { id title } }
Since the GraphQL client automatically adds the __typename
field, the result of this variant immediately tells the cache that Post ID 5 has changed and any cached query results containing the post need to be invalidated:
{ data: { __typename: "Post", id: 5, title: "Working with GraphQL Caching" } }
The next time the user sends the same query, the query will get new data from the source instead of using expired results in the cache.
Many GraphQL clients do not cache the entire query results. Instead, they normalize cached data into two data structures: one associates each object with its data (e.g. Post #5:{…}, User #1:{…}, etc.); the other associates each query with the objects it contains (e.g. getPost:{Post #5, User #1}, etc.).
Please refer to urql's documentation on normalizing caching or Apollo's "Demystifying Cache Normalization" for specific examples and use cases.
One of the main edge cases where GraphQL caches cannot handle automatically is adding items to the list. So if createPost
mutation goes through the cache, it doesn't know which specific list to add the item to.
The easiest "workaround" is to query the parent type in the mutation (if it exists). For example, in the following query, we query the community relationship on the post:
query getPost { post(slug: "working-with-graphql-caching") { id title author { id name avatar } # Also query the community of the post community { id name } } }
Then we can also query the community from createPost
variant and invalidate any cached query results containing the community:
mutation createPost { createPost(input: { ... }) { id title # Also query and thus invalidate the community of the post community { id name } } }
While not perfect, typed patterns and __typename
metafields are the keys to making the GraphQL API great for caching.
You might think all of this is a stopgap solution, and GraphQL still doesn't support traditional caching. You can't be wrong. Since GraphQL runs via POST requests, you need to uninstall to the application client and use the "tricks" mentioned above to take advantage of GraphQL's modern browser cache.
However, this approach is not always possible and is not the best way to manage client caches. For more difficult cases, the GraphQL client requires you to manually update the cache, but services like GraphCDN provide a "server-like" caching experience, and also provide a manual clearing API that allows you to do the following for better caching control:
# Purge all occurrences of a specific object mutation { purgeUser(id: [5]) }
# Purge by query name mutation { _purgeQuery(queries: [listUsers, listPosts]) }
# Purge all occurrences of a type mutation { purgeUser }
Now, no matter where you use the GraphCDN endpoint, you no longer need to reimplement the cache policy in all client logic such as mobile, web, etc. Edge caching makes your API very fast and reduces load by sharing caches between users and separating them from each user’s client.
I recently used GraphCDN in a project which helped me handle cache configuration on the client or server, allowing me to continue with my project. For example, I could replace my endpoint with GraphCDN and get the queries complexity (coming soon), analysis and more for free.
So, does GraphQL care about caching? Of course you care! Not only does it provide some built-in automatic caching methods, but many GraphQL libraries provide other ways to implement and manage caching.
Hopefully this article helps you understand the GraphQL caching mechanism, and how to implement it on the client side, and how to leverage CDN to do all the work. My goal is not to convince you to use GraphQL in all your projects, but if you are choosing a query language and caching is an important factor, know that GraphQL is perfectly competent for this task.
The above is the detailed content of Working With GraphQL Caching. For more information, please follow other related articles on the PHP Chinese website!