Skip to content

Commit

Permalink
Intro for the new key-args.mdx page.
Browse files Browse the repository at this point in the history
  • Loading branch information
benjamn committed Oct 26, 2020
1 parent 996ce18 commit eecbc19
Showing 1 changed file with 23 additions and 4 deletions.
27 changes: 23 additions & 4 deletions docs/source/pagination/key-args.mdx
Original file line number Diff line number Diff line change
@@ -1,3 +1,22 @@
---
title: The keyArgs API
sidebar_title: keyArgs
---

> We recommend reading [Core pagination API](./core-api) before learning about considerations specific to the `keyArgs` configuration.
In GraphQL, a single field within a single object can have many different values at any given time, determined by factors such as the identity of the field's enclosing object, the name of the field, the time of the request, and the names and values of any arguments that were passed to the field as part of the request.

In addition to `merge` and `read` functions, `InMemoryCache` field policies can contain a configuration called [`keyArgs`](../caching/cache-field-behavior#specifying-key-arguments), which specifies an array of argument names whose values should be serialized and appended to the field name to create a distinct storage key for a particular value of the field to be stored in the cache.

> In the unlikely event that a `keyArgs` array is insufficient to specify the storage key, you can alternatively pass a function for `keyArgs`, which allows you to sanitize and serialize the `args` object however you like.
By default, `InMemoryCache` incorporates _all_ field arguments into the storage key for each field, so a single field can simultaneously hold as many different values as the number of unique combinations of arguments. This default strategy sacrifices the hit rate of the cache in order to avoid reusing field values inappropriately when any of the arguments are not the same.

However, in many cases, you may realize that your field values are more reusable than this default strategy assumes, and some or all of the arguments are not actually relevant to the storage identity of the field. A `keyArgs: ["type"]` field policy configuration means `type` is the only argument the cache should consider (in addition to the field name and the identity of the enclosing object) when accessing values for this field. A `keyArgs: false` configuration for a given field disables the whole system of differentiating field values by arguments, so the field's value will be identified only by its enclosing object and the name of the field, without any serialized arguments appended to it.

This article provides specific technical guidance on choosing `keyArgs`, especially when working with paginated fields and field policies.

## Which arguments belong in `keyArgs`?

Throughout this section of documentation, you'll find a number of possible `keyArgs` configurations, ranging from including all arguments by default, to completely disabling argument-based field identification using `keyArgs: false`.
Expand All @@ -6,7 +25,7 @@ To understand which arguments belong in `keyArgs` (if any), it's helpful to cons

If you include all arguments in the field key, as `InMemoryCache` does by default, then every different combination of argument values will correspond to a different storage location for internal field data. In other words, if you change any argument values, the field key will be different, so the field value will be stored in a different location. In your `read` and `merge` functions, this internal field data is provided by the `existing` parameter, which will be undefined when a particular combination of arguments has never been seen before. With this approach, the cache can reuse field values only if the arguments exactly match, which significantly reduces the hit rate of the cache, but also keeps the cache from inappropriately reusing field values when differences in arguments actually matter.

On the other hand, if you configure your field with `keyArgs: false`, the field key will always be just the field name, without any extra characters appended to it. Because your `read` and `merge` functions have access to the field arguments via `options.args`, you could keep your internal data separated according to those arguments, simulating the behavior of `keyArgs` without actually using `keyArgs`. Your `read` function then gets to decide whether an existing field value can be reused, based on the runtime argument values and whatever internal data have previously been stored for the field.
On the other hand, if you configure your field with `keyArgs: false`, the field key will always be just the field name, without any extra characters appended to it. Because your `read` and `merge` functions have access to the field arguments via `options.args`, you could use `read` and `merge` to keep your internal data separated according to the arguments, simulating the behavior of `keyArgs` without actually using `keyArgs`. Your `read` function then gets to decide whether an existing field value can be reused, and how it should be transformed before it is reused, based on the runtime argument values and whatever internal value was previously stored.

For example, we could have used `keyArgs: false` instead of `keyArgs: ["type"]` for our `Query.feed` field policy:

Expand Down Expand Up @@ -40,13 +59,13 @@ const cache = new InMemoryCache({

Instead of a single array, `existing` is now a map from `type`s to feeds, allowing a single field value to store multiple feed arrays, separated by `type`. However, this manual separation is logically equivalent to what would happen if you moved the `type` argument into `keyArgs` (using `keyArgs: ["type"]`, as above), so the extra effort is probably unnecessary.

Assuming feeds with different `type` values are independent, and our `read` function does not need simultaneous access to multiple feeds of different types, we can safely shift the responsibility for handling the `type` argument from the `read` and `merge` functions back to `keyArgs`, and simplify `read` and `merge` to handle only one feed at a time.
Assuming feeds with different `type` values have different data, and assuming our `read` function does not need simultaneous access to multiple feeds of different types, we can safely shift the responsibility for handling the `type` argument from the `read` and `merge` functions back to `keyArgs`, and simplify `read` and `merge` to handle only one feed at a time.

In short, if the logic for storing and retrieving field data is the same for different values of a given argument (like `type`), and those field values are logically independent from one another, then you probably should move that argument into `keyArgs`, to save yourself from having to deal with it in your `read` and `merge` functions.

By contrast, arguments that limit, filter, sort, or otherwise reprocess existing field data usually do not belong in `keyArgs`, because putting them in `keyArgs` fragments the internal data, so you cannot use a different argument value to retrieve a different view of the same data, without making a network request.
By contrast, arguments that limit, filter, sort, or otherwise reprocess existing field data usually do not belong in `keyArgs`, because putting them in `keyArgs` fragments the internal data, so you cannot use different arguments to retrieve different views of the same data, without making a additional network requests.

As a general rule, `read` and `merge` functions can do almost anything with your field data, but there might be a less powerful tool (like `keyArgs`) that allows you to simplify (or avoid writing) custom `read` or `merge` functions. Whenever you have a choice between two capable tools, prefer the one that minimizes the total complexity of your code, while still providing the behavior you desire.
As a general rule, `read` and `merge` functions can do almost anything with your field data, but there might be a less powerful tool (like `keyArgs`) that allows you to simplify (or avoid writing) custom `read` or `merge` functions. Whenever you have a choice between two capable tools, you should prefer the one that minimizes the total complexity of your code, which often favors a more limited, declarative API like `keyArgs`, over the unlimited power of functions like `merge` or `read`.

## The `@connection` directive

Expand Down

0 comments on commit eecbc19

Please sign in to comment.