My problem:
I am pretty new to GraphQL and I am developing my first full stack app using Apollo server and client, which is a simple blog.
On client side, I am using the same query in two different pages, but with different variables. Queries are querying a blog article by ID or slug, depending on the page I am using it. So the result is the same, there is just the queries variables that changes.
When I use a query in one page, I thought query wouldn't run on the second page because of Apollo cache. But it is not what is happening. The query runs again in the second, and of course returns me the same result that in the other page.
Why does Apollo doesn't use the cache in this case?
Here is the code I use :
On server side, I have a pretty basic query to fetch an article from a blog, which can be fetched by ID or Slug:
type Query {
...
article(id: ID, slug: String): Article
...
}
On client side, I query an article by slug if the article is published, or by ID when it is still a draft.
The query by slug:
<Query
query={article}
variables={{ slug }}
fetchPolicy="cache-and-network"
>
{({ loading, error, data }) => {
return (
<Article
loading={loading}
article={data && data.article}
/>
);
}}
</Query>
The query by ID is the same, except the variables param which uses ID:
<Query
query={article}
variables={{ id }}
>
{({ loading, error, data }) => {
return (
<EditArticle loading={loading} article={data && data.article} />
);
}}
</Query>
As you can see, both are using the same GraphQL endpoint, and the result is the same. But the cache is not used.
Apollo assumes that your resolvers are pure (they don't have side effects and formostly return the same result given the same input/arguments). This is already a lot to assume. Imagine a resolver that returns a random number or the newest comment on a news website. Both would not always return the same result given the same input. On the other hand Apollo does not make - and pretty much cannot make - assumptions about the implementation of your resolver. While in your head the implementation for your article resolver is obvious (if the id
is present return article with that id, if slug
is present return article with that slug) this is a lot to ask from a computer programm to guess.
I have answered a similar question recently. To prevent the second query from running you have to implement a cache redirect. The downside is that you have to keep your cache redirects on the client and resolvers on the server in sync.
I have hit this same problem. In essence, I expected the cache lookup to simply fail when it attempts a look up with the "slug" only, and I was fine with that, but instead it fails to generate a correct search result and a "null" result is return as the query response as though it were a successful query response. Oops.
In order to avoid side-effects, I will just be using a separate graphQL query which accepts a slug instead of an ID. This has a couple other benefits, for instance I can enforce the field as "required" in their respective queries. Main thing is that it makes the ID-based query more deterministic and thus more compatible with caching.
type Query {
...
article(id: ID!): Article
articleBySlug(slug: String!): Article
...
}
Even better would be the ability to search the cache using your "slug" value for a matching result but this doesn't seem to be supported yet without using the "slug" as part of the cache ID itself.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With