|
| 1 | +--- |
| 2 | +id: cache-configuration |
| 3 | +title: Configuring the cache |
| 4 | +sidebar_label: Configuration |
| 5 | +--- |
| 6 | + |
| 7 | +The Ferry cache normalizes incoming data for your GraphQL operations and stores it using a `Store`. This allows the client to respond to future queries for the same data without sending unnecessary network requests. |
| 8 | + |
| 9 | +## Data Normalization |
| 10 | + |
| 11 | +Normalization involves the following steps: |
| 12 | + |
| 13 | +1. The cache [generates a unique ID](#generating-unique-identifiers) for every identifiable object included in the response. |
| 14 | +2. The cache stores the objects by ID in a flat lookup table. |
| 15 | +3. Whenever an incoming object is stored with the same ID as an _existing_ object, the fields of those objects are _merged_. |
| 16 | + - If the incoming object and the existing object share any fields, the incoming object _overwrites_ the cached values for those fields. |
| 17 | + - Fields that appear in _only_ the existing object or _only_ the incoming object are preserved. |
| 18 | + |
| 19 | +Normalization constructs a partial copy of your data graph on your client, in a format that's optimized for reading and updating the graph as your application changes state. |
| 20 | + |
| 21 | +### Generating Unique Identifiers |
| 22 | + |
| 23 | +By default, the cache generates a unique identifier using an object's `__typename` and any uniquely identifying fields, called `keyFields`. By default, the `id` or `_id` field (whichever is defined) is used as a `keyField`, but this behavior can be [customized](#customizing-identifier-generation-by-type). These two values are separated by a colon (`:`). |
| 24 | + |
| 25 | +For example, an object with a `__typename` of `Task` and an `id` of `14` is assigned a default identifier of `Task:14`. |
| 26 | + |
| 27 | +#### Customizing Identifier Generation by Type |
| 28 | + |
| 29 | +If one of your types defines its primary key with a field _besides_ `id` or `_id`, you can customize how the cache generates unique identifiers for that type. To do so, you define `TypePolicy` for the type and include a Map of all your `typePolicies` when instantiating the cache. |
| 30 | + |
| 31 | +Include a `keyFields` field in relevant `TypePolicy` objects, like so: |
| 32 | + |
| 33 | +```dart |
| 34 | +final cache = Cache( |
| 35 | + typePolicies: { |
| 36 | + 'Product': TypePolicy( |
| 37 | + // In most inventory management systems, a single UPC code uniquely |
| 38 | + // identifies any product. |
| 39 | + keyFields: { |
| 40 | + 'upc': true, |
| 41 | + }, |
| 42 | + ), |
| 43 | + 'Person': TypePolicy( |
| 44 | + // In some user account systems, names or emails alone do not have to |
| 45 | + // be unique, but the combination of a person's name and email is |
| 46 | + // uniquely identifying. |
| 47 | + keyFields: { |
| 48 | + 'name': true, |
| 49 | + 'email': true, |
| 50 | + }, |
| 51 | + ), |
| 52 | + 'Book': TypePolicy( |
| 53 | + // If one of the keyFields is an object with fields of its own, you can |
| 54 | + // include those nested keyFields by using a nested array of strings: |
| 55 | + keyFields: { |
| 56 | + 'title': true, |
| 57 | + 'author': { |
| 58 | + 'name': true, |
| 59 | + }, |
| 60 | + }, |
| 61 | + ), |
| 62 | + }, |
| 63 | +); |
| 64 | +``` |
| 65 | + |
| 66 | +This example shows three `typePolicies`: one for a `Product` type, one for a `Person` type, and one for a `Book` type. Each `TypePolicy`'s `keyFields` array defines which fields on the type _together_ represent the type's primary key. |
| 67 | + |
| 68 | +The `Book` type above uses a _subfield_ as part of its primary key. The `Book`'s `author` field must be an object that includes a `name` field for this to be valid. |
| 69 | + |
| 70 | +In the example above, the resulting identifier string for a `Book` object has the following structure: |
| 71 | + |
| 72 | +``` |
| 73 | +Book:{"author":{"name":"Ray Bradbury"}, "title":"Fahrenheit 451"} |
| 74 | +``` |
| 75 | + |
| 76 | +An object's primary key fields are always listed alphabetically to ensure uniqueness. |
| 77 | + |
| 78 | +Note that these `keyFields` strings always refer to the actual field names as defined in your schema, meaning the ID computation is not sensitive to field aliases. |
| 79 | + |
| 80 | +### Disabling Normalization |
| 81 | + |
| 82 | +You can instruct Ferry _not_ to normalize objects of a certain type. This can be useful for metrics and other transient data that's identified by a timestamp and never receives updates. |
| 83 | + |
| 84 | +To disable normalization for a type, define a `TypePolicy` for the type (as shown in [Customizing Identifier Generation by Type](#customizing-identifier-generation-by-type)) and set the policy's `keyFields` field to an empty `Map`. |
| 85 | + |
| 86 | +Objects that are not normalized are instead embedded within their _parent_ object in the cache. You can't access these objects directly, but you can access them via their parent. |
| 87 | + |
| 88 | +## `TypePolicy` Fields |
| 89 | + |
| 90 | +To customize how the cache interacts with specific types in your schema, you can provide an object mapping `__typename` strings to `TypePolicy` objects when you create a new `Cache`. |
| 91 | + |
| 92 | +A `TypePolicy` object can include the following fields: |
| 93 | + |
| 94 | +````dart |
| 95 | +class TypePolicy { |
| 96 | + /// Allows defining the primary key fields for this type. |
| 97 | + /// |
| 98 | + /// Pass a `true` value for any fields you wish to use as key fields. You can |
| 99 | + /// also use child fields. |
| 100 | + /// |
| 101 | + /// ```dart |
| 102 | + /// final bookTypePolicy = TypePolicy( |
| 103 | + /// keyFields: { |
| 104 | + /// 'title': true, |
| 105 | + /// 'author': { |
| 106 | + /// 'name': true, |
| 107 | + /// } |
| 108 | + /// }, |
| 109 | + /// ); |
| 110 | + /// ``` |
| 111 | + /// |
| 112 | + /// If you don't wish to normalize this type, simply pass an empty `Map`. In |
| 113 | + /// that case, we won't normalize this type and it will be reachable from its |
| 114 | + /// parent. |
| 115 | + Map<String, dynamic> keyFields; |
| 116 | +
|
| 117 | + /// Set to `true` if this type is the root Query in your schema. |
| 118 | + bool queryType; |
| 119 | +
|
| 120 | + /// Set to `true` if this type is the root Mutation in your schema. |
| 121 | + bool mutationType; |
| 122 | +
|
| 123 | + /// Set to `true` if this type is the root Subscription in your schema. |
| 124 | + bool subscriptionType; |
| 125 | +
|
| 126 | + /// Allows defining [FieldPolicy]s for this type. |
| 127 | + Map<String, FieldPolicy> fields; |
| 128 | +
|
| 129 | + TypePolicy({ |
| 130 | + this.keyFields, |
| 131 | + this.queryType = false, |
| 132 | + this.mutationType = false, |
| 133 | + this.subscriptionType = false, |
| 134 | + this.fields = const {}, |
| 135 | + }); |
| 136 | +} |
| 137 | +```` |
| 138 | + |
| 139 | +### Overriding Root Operation Types (Uncommon) |
| 140 | + |
| 141 | +In addition to `keyFields`, a `TypePolicy` can indicate that it represents the root query, mutation, or subscription type by setting `queryType`, `mutationType`, or `subscriptionType` as `true`: |
| 142 | + |
| 143 | +```dart |
| 144 | +final cache = Cache( |
| 145 | + typePolicies: { |
| 146 | + 'UnconventionalRootQuery': TypePolicy( |
| 147 | + queryType: true, |
| 148 | + ), |
| 149 | + }, |
| 150 | +); |
| 151 | +``` |
| 152 | + |
| 153 | +The cache normally obtains `__typename` information by adding the `__typename` field to every GraphQL operation selection set it sends to the server. The `__typename` of the root query or mutation is almost always simply `"Query"` or `"Mutation"`, so the cache assumes those common defaults unless instructed otherwise in a `TypePolicy`. |
| 154 | + |
| 155 | +### The `fields` Property |
| 156 | + |
| 157 | +The final property within `TypePolicy` is the `fields` property, which is a map from string field names to `FieldPolicy` objects. For more information on this field, see [Customizing the behavior of cached fields](field-policies.md). |
0 commit comments