Mirror: The highly customizable and versatile GraphQL client with which you add on features like normalized caching as you grow.
1--- 2title: '@urql/exchange-graphcache' 3order: 4 4--- 5 6# @urql/exchange-graphcache 7 8> **Note:** These API docs are deprecated as we now keep TSDocs in all published packages. 9> You can view TSDocs while using these packages in your editor, as long as it supports the 10> TypeScript Language Server. 11> We're planning to replace these API docs with a separate web app soon. 12 13The `@urql/exchange-graphcache` package contains an addon `cacheExchange` for `urql` that may be 14used to replace the default [`cacheExchange`](./core.md#cacheexchange), which switches `urql` from 15using ["Document Caching"](../basics/document-caching.md) to ["Normalized 16Caching"](../graphcache/normalized-caching.md). 17 18[Read more about how to use and configure _Graphcache_ in the "Graphcache" 19section](../graphcache/README.md) 20 21## cacheExchange 22 23The `cacheExchange` function, as exported by `@urql/exchange-graphcache`, accepts a single object of 24options and returns an [`Exchange`](./core.md#exchange). 25 26| Input | Description | 27| ------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | 28| `keys` | A mapping of key generator functions for types that are used to override the default key generation that _Graphcache_ uses to normalize data for given types. | 29| `resolvers` | A nested mapping of resolvers, which are used to override the record or entity that _Graphcache_ resolves for a given field for a type. | 30| `directives` | A mapping of directives, which are functions accepting directive arguments and returning a resolver, which can be referenced by `@localDirective` or `@_localDirective` in queries. | 31| `updates` | A nested mapping of updater functions for mutation and subscription fields, which may be used to add side-effects that update other parts of the cache when the given subscription or mutation field is written to the cache. | 32| `optimistic` | A mapping of mutation fields to resolvers that may be used to provide _Graphcache_ with an optimistic result for a given mutation field that should be applied to the cached data temporarily. | 33| `schema` | A serialized GraphQL schema that is used by _Graphcache_ to resolve partial data, interfaces, and enums. The schema also used to provide helpful warnings for [schema awareness](../graphcache/schema-awareness.md). | 34| `storage` | A persisted storage interface that may be provided to preserve cache data for [offline support](../graphcache/offline.md). | 35| `globalIDs` | A boolean or list of typenames that have globally unique ids, this changes how graphcache internally keys the entities. This can be useful for complex interface relationships. | 36| `logger` | A function that will be invoked for warning/debug/... logs | 37 38The `@urql/exchange-graphcache` package also exports the `offlineExchange`; which is identical to 39the `cacheExchange` but activates [offline support](../graphcache/offline.md) when the `storage` option is passed. 40 41### `keys` option 42 43This is a mapping of typenames to `KeyGenerator` functions. 44 45```ts 46interface KeyingConfig { 47 [typename: string]: (data: Data) => null | string; 48} 49``` 50 51It may be used to alter how _Graphcache_ generates the key it uses for normalization for individual 52types. The key generator function may also always return `null` when a type should always be 53embedded. 54 55[Read more about how to set up `keys` in the "Key Generation" section of the "Normalized Caching" 56page.](../graphcache/normalized-caching.md#key-generation) 57 58### `resolvers` option 59 60This configuration is a mapping of typenames to field names to `Resolver` functions. 61A resolver may be defined to override the entity or record that a given field on a type should 62resolve on the cache. 63 64```ts 65interface ResolverConfig { 66 [typeName: string]: { 67 [fieldName: string]: Resolver; 68 }; 69} 70``` 71 72A `Resolver` receives four arguments when it's called: `parent`, `args`, `cache`, and 73`info`. 74 75| Argument | Type | Description | 76| -------- | -------- | ----------------------------------------------------------------------------------------------------------- | 77| `parent` | `Data` | The parent entity that the given field is on. | 78| `args` | `object` | The arguments for the given field the updater is executed on. | 79| `cache` | `Cache` | The cache using which data can be read or written. [See `Cache`.](#cache) | 80| `info` | `Info` | Additional metadata and information about the current operation and the current field. [See `Info`.](#info) | 81 82We can use the arguments it receives to either return new data based on just the arguments and other 83cache information, but we may also read information about the parent and return new data for the 84current field. 85 86```js 87{ 88 Todo: { 89 createdAt(parent, args, cache) { 90 // Read `createdAt` on the parent but return a Date instance 91 const date = cache.resolve(parent, 'createdAt'); 92 return new Date(date); 93 } 94 } 95} 96``` 97 98[Read more about how to set up `resolvers` on the "Computed Queries" 99page.](../graphcache/local-resolvers.md) 100 101### `updates` option 102 103The `updates` configuration is a mapping of `'Mutation' | 'Subscription'` to field names to 104`UpdateResolver` functions. An update resolver may be defined to add side-effects that run when a 105given mutation field or subscription field is written to the cache. These side-effects are helpful 106to update data in the cache that is implicitly changed on the GraphQL API, that _Graphcache_ can't 107know about automatically. 108 109```ts 110interface UpdatesConfig { 111 Mutation: { 112 [fieldName: string]: UpdateResolver; 113 }; 114 Subscription: { 115 [fieldName: string]: UpdateResolver; 116 }; 117} 118``` 119 120An `UpdateResolver` receives four arguments when it's called: `result`, `args`, `cache`, and 121`info`. 122 123| Argument | Type | Description | 124| -------- | -------- | ----------------------------------------------------------------------------------------------------------- | 125| `result` | `any` | Always the entire `data` object from the mutation or subscription. | 126| `args` | `object` | The arguments for the given field the updater is executed on. | 127| `cache` | `Cache` | The cache using which data can be read or written. [See `Cache`.](#cache) | 128| `info` | `Info` | Additional metadata and information about the current operation and the current field. [See `Info`.](#info) | 129 130It's possible to derive more information about the current update using the `info` argument. For 131instance this metadata contains the current `fieldName` of the updater which may be used to make an 132updater function more reusable, along with `parentKey` and other key fields. It also contains 133`variables` and `fragments` which remain the same for the entire write operation, and additionally 134it may have the `error` field set to describe whether the current field is `null` because the API 135encountered a `GraphQLError`. 136 137[Read more about how to set up `updates` on the "Custom Updates" 138page.](../graphcache/cache-updates.md) 139 140### `optimistic` option 141 142The `optimistic` configuration is a mapping of Mutation field names to `OptimisticMutationResolver` 143functions, which return optimistic mutation results for given fields. These results are used by 144_Graphcache_ to optimistically update the cache data, which provides an immediate and temporary 145change to its data before a mutation completes. 146 147```ts 148interface OptimisticMutationConfig { 149 [mutationFieldName: string]: OptimisticMutationResolver; 150} 151``` 152 153A `OptimisticMutationResolver` receives three arguments when it's called: `variables`, `cache`, and 154`info`. 155 156| Argument | Type | Description | 157| -------- | -------- | ----------------------------------------------------------------------------------------------------------- | 158| `args` | `object` | The arguments that the given mutation field received. | 159| `cache` | `Cache` | The cache using which data can be read or written. [See `Cache`.](#cache) | 160| `info` | `Info` | Additional metadata and information about the current operation and the current field. [See `Info`.](#info) | 161 162[Read more about how to set up `optimistic` on the "Custom Updates" 163page.](../graphcache/cache-updates.md) 164 165### `schema` option 166 167The `schema` option may be used to pass a `IntrospectionQuery` data to _Graphcache_, in other words 168it's used to provide schema information to it. This schema is then used to resolve and return 169partial results when querying, which are results that the cache can partially resolve as long as no 170required fields are missing. 171 172[Read more about how to use the `schema` option on the "Schema Awareness" 173page.](../graphcache/schema-awareness.md) 174 175### `storage` option 176 177The `storage` option is an interface of methods that are used by the `offlineExchange` to persist 178the cache's data to persisted storage on the user's device. it 179 180> **NOTE:** Offline Support is currently experimental! It hasn't been extensively tested yet and 181> may not always behave as expected. Please try it out with caution! 182 183| Method | Type | Description | 184| ----------------- | --------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | 185| `writeData` | `(delta: SerializedEntries) => Promise<void>` | This provided method must be able to accept an object of key-value entries that will be persisted to the storage. This method is called as a batch of updated entries becomes ready. | 186| `readData` | `() => Promise<SerializedEntries>` | This provided method must be able to return a single combined object of previous key-value entries that have been previously preserved using `writeData`. It's only called on startup. | 187| `writeMetadata` | `(json: SerializedRequest[]) => void` | This provided method must be able to persist metadata for the cache. For backwards compatibility it should be able to accept any JSON data. | 188| `readMetadata` | `() => Promise<null \| SerializedRequest[]>` | This provided method must be able to read the persisted metadata that has previously been written using `writeMetadata`. It's only called on startup. | 189| `onOnline` | `(cb: () => void) => void` | This method must be able to accept a callback that is called when the user's device comes back online. | 190| `onCacheHydrated` | `() => void` | This method will be called when the `cacheExchange` has finished hydrating the data coming from storage. | 191 192These options are split into three parts: 193 194- The `writeMetadata` and `readMetadata` methods are used to persist in-progress optimistic 195 mutations to a storage so that they may be retried if the app has been closed while some 196 optimistic mutations were still in progress. 197- The `writeData` and `readData` methods are used to persist any cache data. This is the normalized 198 data that _Graphcache_ usually keeps in memory. The `cacheExchange` will frequently call 199 `writeData` with a partial object of its cache data, which `readData` must then be able to return 200 in a single combined object on startup. We call the partial objects that `writeData` is called 201 with "deltas". 202- The `onOnline` method is only used to receive a trigger that determines whether the user's device 203 has come back online, which is used to retry optimistic mutations that have previously failed due 204 to being offline. 205 206The `storage` option may also be used with the `cacheExchange` instead of the `offlineExchange`, but 207will then only use `readData` and `writeData` to persist its cache data. This is not full offline 208support, but will rather be "persistence support". 209 210[Read more about how to use the `storage` option on the "Offline Support" 211page.](../graphcache/offline.md) 212 213## Cache 214 215An instance of the `Cache` interface is passed to every resolvers and updater function. It may be 216used to read cached data or write cached data, which may be used in combination with the 217[`cacheExchange` configuration](#cacheexchange) to alter the default behaviour of _Graphcache_. 218 219### keyOfEntity 220 221The `cache.keyOfEntity` method may be called with a partial `Data` object and will return the key 222for that object, or `null` if it's not keyable. 223 224An object may not be keyable if it's missing the `__typename` or `id` (which falls back to `_id`) 225fields. This method does take the [`keys` configuration](#keys-option) into account. 226 227```js 228cache.keyOfEntity({ __typename: 'Todo', id: 1 }); // 'Todo:1' 229cache.keyOfEntity({ __typename: 'Query' }); // 'Query' 230cache.keyOfEntity({ __typename: 'Unknown' }); // null 231``` 232 233There's an alternative method, `cache.keyOfField` which generates a key for a given field. This is 234only rarely needed but similar to `cache.keyOfEntity`. This method accepts a field name and 235optionally a field's arguments. 236 237```js 238cache.keyOfField('todo'); // 'todo' 239cache.keyOfField('todo', { id: 1 }); // 'todo({"id":1})' 240``` 241 242Internally, these are the keys that records and links are stored on per entity. 243 244### resolve 245 246This method retrieves a value or link for a given field, given a partially keyable `Data` object or 247entity, a field name, and optionally the field's arguments. Internally this method accesses the 248cache by using `cache.keyOfEntity` and `cache.keyOfField`. 249 250```js 251// This may resolve a link: 252cache.resolve({ __typename: 'Query' }, 'todo', { id: 1 }); // 'Todo:1' 253 254// This may also resolve records / scalar values: 255cache.resolve({ __typename: 'Todo', id: 1 }, 'id'); // 1 256 257// You can also chain multiple calls to `cache.resolve`! 258cache.resolve(cache.resolve({ __typename: 'Query' }, 'todo', { id: 1 }), 'id'); // 1 259``` 260 261As you can see in the last example of this code snippet, the `Data` object can also be replaced by 262an entity key, which makes it possible to pass a key from `cache.keyOfEntity` or another call to 263`cache.resolve` instead of the partial entity. 264 265> **Note:** Because `cache.resolve` may return either a scalar value or another entity key, it may 266> be dangerous to use in some cases. It's a good idea to make sure first whether the field you're 267> reading will be a key or a value. 268 269The `cache.resolve` method may also be called with a field key as generated by `cache.keyOfField`. 270 271```js 272cache.resolve({ __typename: 'Query' }, cache.keyOfField('todo', { id: 1 })); // 'Todo:1' 273``` 274 275This specialized case is likely only going to be useful in combination with 276[`cache.inspectFields`](#inspectfields). 277 278### inspectFields 279 280The `cache.inspectFields` method may be used to interrogate the cache about all available fields on 281a specific entity. It accepts a partial entity or an entity key, like [`cache.resolve`](#resolve)'s 282first argument. 283 284When calling the method this returns an array of `FieldInfo` objects, one per field (including 285differing arguments) that is known to the cache. The `FieldInfo` interface has three properties: 286`fieldKey`, `fieldName`, and `arguments`: 287 288| Argument | Type | Description | 289| ----------- | ---------------- | ------------------------------------------------------------------------------- | 290| `fieldName` | `string` | The field's name (without any arguments, just the name) | 291| `arguments` | `object \| null` | The field's arguments, or `null` if the field doesn't have any arguments | 292| `fieldKey` | `string` | The field's cache key, which is similar to what `cache.keyOfField` would return | 293 294This works on any given entity. When calling this method the cache works in reverse on its data 295structure, by parsing the entity's individual field keys. 296p 297 298```js 299cache.inspectFields({ __typename: 'Query' }); 300 301/* 302 [ 303 { fieldName: 'todo', arguments: { id: 1 }, fieldKey: 'id({"id":1})' }, 304 { fieldName: 'todo', arguments: { id: 2 }, fieldKey: 'id({"id":2})' }, 305 ... 306 ] 307*/ 308``` 309 310### readFragment 311 312`cache.readFragment` accepts a GraphQL `DocumentNode` as the first argument and a partial entity or 313an entity key as the second, like [`cache.resolve`](#resolve)'s first argument. 314 315The method will then attempt to read the entity according to the fragment entirely from the cached 316data. If any data is uncached and missing it'll return `null`. 317 318```js 319import { gql } from '@urql/core'; 320 321cache.readFragment( 322 gql` 323 fragment _ on Todo { 324 id 325 text 326 } 327 `, 328 { id: 1 } 329); // Data or null 330``` 331 332Note that the `__typename` may be left out on the partial entity if the fragment isn't on an 333interface or union type, since in that case the `__typename` is already present on the fragment 334itself. 335 336If any fields on the fragment require variables, you can pass them as the third argument like so: 337 338```js 339import { gql } from '@urql/core'; 340 341cache.readFragment( 342 gql` 343 fragment _ on User { 344 id 345 permissions(byGroupId: $groupId) 346 } 347 `, 348 { id: 1 }, // this identifies the fragment (User) entity 349 { groupId: 5 } // any additional field variables 350); 351``` 352 353If you need a specific fragment in a document containing multiple you can leverage 354the fourth argument like this: 355 356```js 357import { gql } from '@urql/core'; 358 359cache.readFragment( 360 gql` 361 fragment todoFields on Todo { 362 id 363 } 364 365 fragment userFields on User { 366 id 367 } 368 `, 369 { id: 1 }, // this identifies the fragment (User) entity 370 undefined, 371 'userFields' // if not passed we take the first fragment, in this case todoFields 372); 373``` 374 375[Read more about using `readFragment` on the ["Local Resolvers" 376page.](../graphcache/local-resolvers.md#reading-a-fragment) 377 378### readQuery 379 380The `cache.readQuery` method is similar to `cache.readFragment`, but instead of reading a fragment 381from cache, it reads an entire query. The only difference between how these two methods are used is 382`cache.readQuery`'s input, which is an object instead of two arguments. 383 384The method accepts a `{ query, variables }` object as the first argument, where `query` may either 385be a `DocumentNode` or a `string` and variables may optionally be an object. 386 387```js 388cache.readQuery({ 389 query: ` 390 query ($id: ID!) { 391 todo(id: $id) { id, text } 392 } 393 `, 394 variables: { 395 id: 1 396 } 397); // Data or null 398``` 399 400[Read more about using `readQuery` on the ["Local Resolvers" 401page.](../graphcache/local-resolvers.md#reading-a-query) 402 403### link 404 405Corresponding to [`cache.resolve`](#resolve), the `cache.link` method allows 406links in the cache to be updated. While the `cache.resolve` method reads both 407records and links from the cache, the `cache.link` method will only ever write 408links as fragments (See [`cache.writeFragment`](#writefragment) below) are more 409suitable for updating scalar data in the cache. 410 411The arguments for `cache.link` are identical to [`cache.resolve`](#resolve) and 412the field's arguments are optional. However, the last argument must always be 413a link, meaning `null`, an entity key, a keyable entity, or a list of these. 414 415In other words, `cache.link` accepts an entity to write to as its first argument, 416with the same arguments as `cache.keyOfEntity`. It then accepts one or two arguments 417that are passed to `cache.keyOfField` to get the targeted field key. And lastly, 418you may pass a list or a single entity (or an entity key). 419 420```js 421// Link Query.todo field to a todo item 422cache.link({ __typename: 'Query' }, 'todo', { __typename: 'Todo', id: 1 }); 423 424// You may also pass arguments instead: 425cache.link({ __typename: 'Query' }, 'todo', { id: 1 }, { __typename: 'Todo', id: 1 }); 426 427// Or use entity keys instead of the entities themselves: 428cache.link('Query', 'todo', cache.keyOfEntity({ __typename: 'Todo', id: 1 })); 429``` 430 431The method may [output a 432warning](../graphcache/errors.md#12-cant-generate-a-key-for-writefragment-or-link) when any of the 433entities were passed as objects but aren't keyable, which is useful when a scalar or a non-keyable 434object have been passed to `cache.link` accidentally. 435 436### writeFragment 437 438Corresponding to [`cache.readFragment`](#readfragments), the `cache.writeFragment` method allows 439data in the cache to be updated. 440 441The arguments for `cache.writeFragment` are identical to [`cache.readFragment`](#readfragment), 442however the second argument, `data`, should not only contain properties that are necessary to derive 443an entity key from the given data, but also the fields that will be written: 444 445```js 446import { gql } from '@urql/core'; 447 448cache.writeFragment( 449 gql` 450 fragment _ on Todo { 451 text 452 } 453 `, 454 { id: 1, text: 'New Todo Text' } 455); 456``` 457 458In the example we can see that the `writeFragment` method returns `undefined`. Furthermore we pass 459`id` in our `data` object so that an entity key can be written, but the fragment itself doesn't have 460to include these fields. 461 462If you need a specific fragment in a document containing multiple you can leverage 463the fourth argument like this: 464 465```js 466import { gql } from '@urql/core'; 467 468cache.writeFragment( 469 gql` 470 fragment todoFields on Todo { 471 id 472 text 473 } 474 475 fragment userFields on User { 476 id 477 name 478 } 479 `, 480 { id: 1, name: 'New Name' } 481 undefined, 482 'userFields' // if not passed we take the first fragment, in this case todoFields 483); 484``` 485 486[Read more about using `writeFragment` on the ["Custom Updates" 487page.](../graphcache/cache-updates.md#cachewritefragment) 488 489### updateQuery 490 491Similarly to [`cache.writeFragment`](#writefragment), there's an analogous method for 492[`cache.readQuery`](#readquery) that may be used to update query data. 493 494The `cache.updateQuery` method accepts the same `{ query, variables }` object input as its first 495argument, which is the query we'd like to write to the cache. As a second argument the method 496accepts an updater function. This function will be called with the query data that is already in the 497cache (which may be `null` if the data is uncached) and must return the new data that should be 498written to the cache. 499 500```js 501const TodoQuery = ` 502 query ($id: ID!) { 503 todo(id: $id) { id, text } 504 } 505`; 506 507cache.updateQuery({ query: TodoQuery, variables: { id: 1 } }, data => { 508 if (!data) return null; 509 data.todo.text = 'New Todo Text'; 510 return data; 511}); 512``` 513 514As we can see, our updater may return `null` to cancel updating any data, which we do in case the 515query data is uncached. 516 517We can also see that data can simply be mutated and doesn't have to be altered immutably. This is 518because all data from the cache is already a deep copy and hence we can do to it whatever we want. 519 520[Read more about using `updateQuery` on the "Custom Updates" 521page.](../graphcache/cache-updates.md#cacheupdatequery) 522 523### invalidate 524 525The `cache.invalidate` method can be used to delete (i.e. "evict") an entity from the cache 526entirely. This will cause it to disappear from all queries in _Graphcache_. 527 528Its arguments are identical to [`cache.resolve`](#resolve). 529 530Since deleting an entity will lead to some queries containing missing and uncached data, calling 531`invalidate` may lead to additional GraphQL requests being sent, unless you're using [_Graphcache_'s 532"Schema Awareness" feature](../graphcache/schema-awareness.md), which takes optional fields into 533account. 534 535This method accepts a partial entity or an entity key as its first argument, similar to 536[`cache.resolve`](#resolve)'s first argument. 537 538```js 539cache.invalidate({ __typename: 'Todo', id: 1 }); // Invalidates Todo:1 540``` 541 542Additionally `cache.invalidate` may be used to delete specific fields only, which can be useful when 543for instance a list is supposed to be evicted from cache, where a full invalidation may be 544impossible. This is often the case when a field on the root `Query` needs to be deleted. 545 546This method therefore accepts two additional arguments, similar to [`cache.resolve`](#resolve). 547 548```js 549// Invalidates `Query.todos` with the `first: 10` argument: 550cache.invalidate('Query', 'todos', { first: 10 }); 551``` 552 553## Info 554 555This is a metadata object that is passed to every resolver and updater function. It contains basic 556information about the current GraphQL document and query, and also some information on the current 557field that a given resolver or updater is called on. 558 559| Argument | Type | Description | 560| ---------------- | -------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | 561| `parent` | `Data` | The field's parent entity's data, as it was written or read up until now, which means it may be incomplete. [Use `cache.resolve`](#resolve) to read from it. | 562| `parentTypeName` | `string` | The field's parent entity's typename | 563| `parentKey` | `string` | The field's parent entity's cache key (if any) | 564| `parentFieldKey` | `string` | The current key's cache key, which is the parent entity's key combined with the current field's key (This is mostly obsolete) | 565| `fieldName` | `string` | The current field's name | 566| `fragments` | `{ [name: string]: FragmentDefinitionNode }` | A dictionary of fragments from the current GraphQL document | 567| `variables` | `object` | The current GraphQL operation's variables (may be an empty object) | 568| `error` | `GraphQLError \| undefined` | The current GraphQLError for a given field. This will always be `undefined` for resolvers and optimistic updaters, but may be present for updaters when the API has returned an error for a given field. | 569| `partial` | `?boolean` | This may be set to `true` at any point in time (by your custom resolver or by _Graphcache_) to indicate that some data is uncached and missing | 570| `optimistic` | `?boolean` | This is only `true` when an optimistic mutation update is running | 571 572> **Note:** Using `info` is regarded as a last resort. Please only use information from it if 573> there's no other solution to get to the metadata you need. We don't regard the `Info` API as 574> stable and may change it with a simple minor version bump. 575 576## The `/extras` import 577 578The `extras` subpackage is published with _Graphcache_ and contains helpers and utilities that don't 579have to be included in every app or aren't needed by all users of _Graphcache_. 580All utilities from extras may be imported from `@urql/exchange-graphcache/extras`. 581 582Currently the `extras` subpackage only contains the [pagination resolvers that have been mentioned 583on the "Computed Queries" page.](../graphcache/local-resolvers.md#pagination) 584 585### simplePagination 586 587Accepts a single object of optional options and returns a resolver that can be inserted into the 588[`cacheExchange`'s](#cacheexchange) [`resolvers` configuration.](#resolvers-option) 589 590| Argument | Type | Description | 591| ---------------- | --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ | 592| `offsetArgument` | `?string` | The field arguments' property, as passed to the resolver, that contains the current offset, i.e. the number of items to be skipped. Defaults to `'skip'`. | 593| `limitArgument` | `?string` | The field arguments' property, as passed to the resolver, that contains the current page size limit, i.e. the number of items on each page. Defaults to `'limit'`. | 594| `mergeMode` | `'after' \| 'before'` | This option defines whether pages are merged before or after preceding ones when paginating. Defaults to `'after'`. | 595 596Once set up, the resulting resolver is able to automatically concatenate all pages of a given field 597automatically. Queries to this resolvers will from then on only return the infinite, combined list 598of all pages. 599 600[Read more about `simplePagination` on the "Computed Queries" 601page.](../graphcache/local-resolvers.md#simple-pagination) 602 603### relayPagination 604 605Accepts a single object of optional options and returns a resolver that can be inserted into the 606[`cacheExchange`'s](#cacheexchange) [`resolvers` configuration.](#resolvers-option) 607 608| Argument | Type | Description | 609| ----------- | ------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | 610| `mergeMode` | `'outwards' \| 'inwards'` | With Relay pagination, pages can be queried forwards and backwards using `after` and `before` cursors. This option defines whether pages that have been queried backwards should be concatenated before (outwards) or after (inwards) all pages that have been queried forwards. | 611 612Once set up, the resulting resolver is able to automatically concatenate all pages of a given field 613automatically. Queries to this resolvers will from then on only return the infinite, combined list 614of all pages. 615 616[Read more about `relayPagnation` on the "Computed Queries" 617page.](../graphcache/local-resolvers.md#relay-pagination) 618 619## The `/default-storage` import 620 621The `default-storage` subpackage is published with _Graphcache_ and contains a default storage 622interface that may be used with the [`storage` option.](#storage-option) 623 624It contains the `makeDefaultStorage` export which is a factory function that accepts a few options 625and returns a full [storage interface](#storage-option). This storage by default persists to 626[IndexedDB](https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API). 627 628| Argument | Type | Description | 629| --------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------- | 630| `idbName` | `string` | The name of the IndexedDB database that is used and created if needed. By default this is set to `"graphcache-v3"` | 631| `maxAge` | `number` | The maximum age of entries that the storage should use in whole days. By default the storage will discard entries that are older than seven days. |