The core concept behind GQty is simple, you interact with the cache while it automatically fetches missing data on-the-fly.
While it is handy for a centralized group of cache accessors that groups all selections and fetches as much as possible in one go, there are use cases where separation is necessary.
It is especially important when implementing persisted queries and operation names. Selections under these context cannot be mixed up with other queries under a different one.
Scoped contexts contain the following elements, which may affect how Selections are combined and ultimately fetched.
- Operation Name
- Cache Policy
Suspense is the most important consideration point if you have Suspense enabled. It determines which level and how many levels in the component tree a query may be suspended and fetched, it also decides the error boundaries of your app.
When making selections by interacting with the cache, GQty captures what is being accessed, combine selections with the same context, fetching them with as few requests as possible.
In React, the
useQuery() hook creates a new scoped context for each
component. Scoped contexts split up selections which sometimes may not be what
you wanted. Instead, you should pass query objects down the component tree as
if their data already fetched. This allows selections to be grouped into one
In React, components create live subscriptions to the current cache even without GraphQL Subscriptions involved. This allows most of the normalization magic happens when a seemingly unrelated changes or fetch from other components arrives, all components reaching such objects will also be updated.
Refetching in GQty does not necessarily means a network request. A refetch first checks the cache freshness of accessed data, it fetches only on cache miss AND the current cache policy allows it.
Consider soft refetches a cache validity check instead, this allows more
frequent refetching strategies. Our
useQuery() hook has options for automatic
refetches on window focus, network reconnect and even periodic attempts.
To manually trigger a soft refetch, use
useQuery().$refetch(false) in React.
Users may specify
no-store as the cache policy in core, or
useQuery().$refetch(true) in React to bypass a fresh cache and
forces a network request.
When designing an expiring cache with stale-while-revalidate (SWR) support,
there is an important concept inherited from the Web standards:
Request.cache (opens in a new tab).
Request.cache is the strategy dealing with the current cache, before fetching
GraphQL servers usually do not hint about cache lifetime for the data, so it is up to developers who make correct assumptions on each and every one of their own projects.
|Serves the cached contents when it is fresh, and if they are stale within the stale-while-revalidate (SWR) window, fetches in the background and updates the cache. Always fetches on stale cache or cache miss. |
During SWR, a successful fetch will not notify cache updates. New contents are served on next query.
|Always fetch, updates client cache on response.|
|Same as |
|Always fetch and does not update on response. |
GQty creates a temporary cache at query-level which immediately expires.
|Serves the cached contents regardless of staleness. It fetches on cache miss or a stale cache, updates the cache on response.|
|Serves the cached contents regardless of staleness, throws a network error on cache miss.|
Our React package has the option
fetchPolicy which is heavily inspired by
the Apollo Client. But over time, we found the cache option in the Fetch API
more concise and easier to understand across multiple APIs.
Cache normalization is best explained with sketches.
Given two data trees in cache, both containing node "a".
They will be merged into the same object reference, and kept in a global store.
This global store will be serialized along with the main data cache during persistence and rehydration.
When a node is part of an updating tree, subscribers of other trees referencing the same node will also be notified.
Otherwise maintaining a reverse lookup map, normalized object references inside query caches will be a proxy of the actual data inside the global store.
When objects are updated or replaced, references to the proxies won't change only underlying objects are swapped.