@nerdwallet/apollo-cache-policies
Advanced tools
Comparing version 3.0.2 to 3.1.0
@@ -0,1 +1,5 @@ | ||
3.1.0 (Dan Reynolds) | ||
Add support for a `limit` field to `fragmentWhere` APIs. | ||
3.0.2 (Dan Reynolds) | ||
@@ -2,0 +6,0 @@ |
@@ -51,2 +51,3 @@ import { InMemoryCache, Cache, NormalizedCacheObject, Reference, StoreObject } from "@apollo/client/core"; | ||
filter?: FragmentWhereFilter<FragmentType>; | ||
limit?: number; | ||
}): FragmentType[]; | ||
@@ -56,2 +57,3 @@ readReferenceWhere<T>(options: { | ||
filter?: FragmentWhereFilter<T>; | ||
limit?: number; | ||
}): readonly Reference[]; | ||
@@ -58,0 +60,0 @@ writeFragmentWhere<FragmentType, TVariables = any>(options: Cache.ReadFragmentOptions<FragmentType, TVariables> & { |
@@ -21,2 +21,4 @@ "use strict"; | ||
const pick_1 = __importDefault(require("lodash/pick")); | ||
const take_1 = __importDefault(require("lodash/take")); | ||
const isNil_1 = __importDefault(require("lodash/isNil")); | ||
const isFunction_1 = __importDefault(require("lodash/isFunction")); | ||
@@ -419,3 +421,3 @@ const InvalidationPolicyManager_1 = __importDefault(require("../policies/InvalidationPolicyManager")); | ||
readFragmentWhere(options) { | ||
const { fragment, filter } = options, restOptions = __rest(options, ["fragment", "filter"]); | ||
const { fragment, filter, limit } = options, restOptions = __rest(options, ["fragment", "filter", "limit"]); | ||
const fragmentDefinition = fragment.definitions[0]; | ||
@@ -425,3 +427,4 @@ const __typename = fragmentDefinition.typeCondition.name.value; | ||
__typename, | ||
filter | ||
filter, | ||
limit, | ||
}); | ||
@@ -434,3 +437,3 @@ const matchingFragments = matchingRefs.map(ref => this.readFragment(Object.assign(Object.assign({}, restOptions), { fragment, id: ref.__ref }))); | ||
readReferenceWhere(options) { | ||
const { __typename, filter } = options; | ||
const { __typename, filter, limit } = options; | ||
const collectionEntityId = (0, utils_1.collectionEntityIdForType)(__typename); | ||
@@ -448,3 +451,3 @@ // If a stale collection is accessed while it has a pending update, then eagerly update it before the read. | ||
} | ||
return entityReferences.filter(ref => { | ||
const filteredReferences = entityReferences.filter(ref => { | ||
if ((0, isFunction_1.default)(filter)) { | ||
@@ -461,2 +464,6 @@ return filter(ref, this.readField.bind(this)); | ||
}); | ||
if (!(0, isNil_1.default)(limit)) { | ||
return (0, take_1.default)(filteredReferences, limit); | ||
} | ||
return filteredReferences; | ||
} | ||
@@ -463,0 +470,0 @@ writeFragmentWhere(options) { |
@@ -64,4 +64,6 @@ "use strict"; | ||
const filterVar = (0, core_1.makeVar)(options.filter); | ||
const limitVar = (0, core_1.makeVar)(options.limit); | ||
const query = (0, utils_1.buildWatchFragmentWhereQuery)(Object.assign(Object.assign({}, options), { fieldName, | ||
filterVar, cache: this.cache, policies: this.policies })); | ||
filterVar, | ||
limitVar, cache: this.cache, policies: this.policies })); | ||
return this.watchQueryForField(query, fieldName); | ||
@@ -68,0 +70,0 @@ } |
@@ -10,3 +10,4 @@ import { DocumentNode } from 'graphql'; | ||
filter?: FragmentWhereFilter<FragmentType>; | ||
limit?: number; | ||
}; | ||
//# sourceMappingURL=types.d.ts.map |
@@ -15,4 +15,5 @@ import { DocumentNode } from 'graphql'; | ||
filterVar: ReactiveVar<FragmentWhereFilter<FragmentType> | undefined>; | ||
limitVar: ReactiveVar<number | undefined>; | ||
fieldName: string; | ||
}): DocumentNode; | ||
//# sourceMappingURL=utils.d.ts.map |
@@ -67,3 +67,3 @@ "use strict"; | ||
function buildWatchFragmentWhereQuery(options) { | ||
const { fragment, filterVar, policies, cache, fieldName, } = options; | ||
const { fragment, filterVar, limitVar, policies, cache, fieldName, } = options; | ||
const fragmentDefinition = fragment.definitions[0]; | ||
@@ -87,2 +87,3 @@ const __typename = fragmentDefinition.typeCondition.name.value; | ||
filter: filterVar(), | ||
limit: limitVar(), | ||
}); | ||
@@ -89,0 +90,0 @@ } |
@@ -6,2 +6,3 @@ import { DocumentNode } from 'graphql'; | ||
returnPartialData?: boolean; | ||
limit?: number; | ||
}): { | ||
@@ -8,0 +9,0 @@ data: FragmentType[]; |
@@ -15,2 +15,3 @@ "use strict"; | ||
const filter = options === null || options === void 0 ? void 0 : options.filter; | ||
const limit = options === null || options === void 0 ? void 0 : options.limit; | ||
const context = (0, react_1.useContext)((0, client_1.getApolloContext)()); | ||
@@ -21,2 +22,4 @@ const client = context.client; | ||
const filterVarRef = (0, react_1.useRef)((0, client_2.makeVar)(filter)); | ||
const limitVarRef = (0, react_1.useRef)((0, client_2.makeVar)(limit)); | ||
const limitVar = limitVarRef.current; | ||
const filterVar = filterVarRef.current; | ||
@@ -29,5 +32,11 @@ const emptyValue = (0, react_1.useRef)([]); | ||
}, [filter]); | ||
(0, react_1.useEffect)(() => { | ||
if (limit !== limitVar()) { | ||
limitVar(limit); | ||
} | ||
}, [limit]); | ||
const query = (0, utils_2.useOnce)(() => (0, utils_1.buildWatchFragmentWhereQuery)({ | ||
filter, | ||
filterVar, | ||
limitVar, | ||
fragment, | ||
@@ -34,0 +43,0 @@ fieldName, |
{ | ||
"name": "@nerdwallet/apollo-cache-policies", | ||
"version": "3.0.2", | ||
"version": "3.1.0", | ||
"description": "An extension to the InMemoryCache from Apollo that adds additional cache policies.", | ||
@@ -5,0 +5,0 @@ "main": "dist/index.js", |
268
README.md
@@ -100,2 +100,136 @@ ![Build](https://github.com/NerdWalletOSS/apollo-cache-policies/workflows/Build/badge.svg) | ||
<summary> | ||
Normalized Collections | ||
</summary> | ||
<br> | ||
## Summary | ||
Normalized collections introduce ways of accessing and filtering all entities in the cache of a given type. They are useful for scenarios where clients may want to access all entities in the cache of a particular type matching a set of filters like a list of all products to show or all the messages of a conversation. To read more about the motivation for this feature, check out [our blog post](https://danreynolds.ca/tech/2021/09/23/Apollo-Normalized-Collections/). | ||
To use normalized collections, enable it in the cache with the collections flag below: | ||
```javascript | ||
import { InvalidationPolicyCache } from '@nerdwallet/apollo-cache-policies'; | ||
const cache = new InvalidationPolicyCache({ | ||
enableCollections: true, | ||
typePolicies: {...}, | ||
invalidationPolicies: {...} | ||
}); | ||
``` | ||
## Specification | ||
Normalized collections introduce 4 new APIs: | ||
1. `useFragmentWhere`: A new React hook for filtering a collection of entities by type | ||
2. `cache.readReferenceWhere`: A cache API that returns a list of references in the cache for a particular type and filter | ||
3. `cache.readFragmentWhere`: The collection filter equivalent of the existing cache.readFragment API | ||
4. `cache.watchFragmentWhere`: The collection filter equivalent of the existing cache.watchFragment API | ||
## useFragmentWhere | ||
The `useFragmentWhere` API allows us to query for a filtered collection of entities by type. It takes two arguments, a GraphQL fragment for the fields to read from the type and an object of all the fields to filter by. | ||
### Example Usage | ||
Now our client can filter all entites of a particular type in the cache like `Employee` in one operation without having to write any type policies. | ||
```js | ||
import { useFragmentWhere } from '@nerdwallet/apollo-cache-policies'; | ||
const { data } = useFragmentWhere( | ||
gql` | ||
fragment EmployeesByTeam on Employee { | ||
id | ||
name | ||
} | ||
`, | ||
{ | ||
filter: { team: 'Banking' } | ||
} | ||
) | ||
``` | ||
If we just want to retrieve all entities in the cache for a particular type, we can omit the filter altogether: | ||
```js | ||
import { useFragmentWhere } from '@nerdwallet/apollo-cache-policies'; | ||
const { data } = useFragmentWhere( | ||
gql` | ||
fragment AllEmployees on Employee { | ||
id | ||
name | ||
} | ||
` | ||
) | ||
``` | ||
The `useFragmentWhere` API will automatically update the component just like `useQuery` when the employees that match the filter change, including when a new employee that matches the filter criteria is added to the cache. | ||
> Note: `useFragmentWhere` subscribes to data changes based on the fragment name you provide, so to return different data from different calls to the API you will want to use different fragment names. | ||
## Cache.readReferenceWhere | ||
Normalized collections can be accessed in type policies using the new `cache.readReferenceWhere` API. `readReferenceWhere` will return a list of references for a given type and filter. | ||
### Example Usage | ||
```js | ||
const cache = new InMemoryCache({ | ||
typePolicies: { | ||
Query: { | ||
fields: { | ||
readBankingTeam: { | ||
read(_existingBankingTeam, { cache }) { | ||
return cache.readReferenceWhere<Employee>( | ||
{ | ||
__typename: 'Employee', | ||
filter: { | ||
team: 'Banking', | ||
}, | ||
} | ||
); | ||
} | ||
}, | ||
}, | ||
}, | ||
}, | ||
}); | ||
``` | ||
In this example, we use the `readReferenceWhere` API to construct a type policy that returns all entities of the `Employee` type in the cache with a field `team` matching the value `Banking`. Any number of fields can be used as filters and queries for this type policy will automatically update whenever an employee entity is added, created removed from the cache. | ||
</details> | ||
<details> | ||
<summary> | ||
Cached Reactive Variables | ||
</summary> | ||
<br> | ||
## Summary | ||
Reactive variables are a powerful and lightweight API for managing local state with Apollo. In cases where client state should be persisted across sessions, it would be helpful to be able to persist reactive variables as well. | ||
Cached reactive variables work the same as regular ones, with the additional function of writing their current value to the cache. Applications still need to set up their own cache persistence using tools like [Apollo Cache Persist](https://github.com/apollographql/apollo-cache-persist). Once cache persistence is in place, cached reactive variables will be rehydrated on new sessions with a runtime value from the cache. | ||
## Example Usage | ||
The only difference in the API when working with cached reactive variables is that a unique ID must be specified for caching. They can then be initialized with a default value, read and written to using the same APIs | ||
as other reactive variables. | ||
```javascript | ||
import { makeCachedVar } from '@nerdwallet/apollo-cache-policies'; | ||
const rv = makeCachedVar('identifier', false); | ||
rv(true); | ||
console.log(rv()); // true | ||
``` | ||
</details> | ||
<details> | ||
<summary> | ||
Invalidation Policies | ||
@@ -240,135 +374,1 @@ </summary> | ||
</details> | ||
<details> | ||
<summary> | ||
Normalized Collections | ||
</summary> | ||
<br> | ||
## Summary | ||
Normalized collections introduce ways of accessing and filtering all entities in the cache of a given type. They are useful for scenarios where clients may want to access all entities in the cache of a particular type matching a set of filters like a list of all products to show or all the messages of a conversation. To read more about the motivation for this feature, check out [our blog post](https://danreynolds.ca/tech/2021/09/23/Apollo-Normalized-Collections/). | ||
To use normalized collections, enable it in the cache with the collections flag below: | ||
```javascript | ||
import { InvalidationPolicyCache } from '@nerdwallet/apollo-cache-policies'; | ||
const cache = new InvalidationPolicyCache({ | ||
enableCollections: true, | ||
typePolicies: {...}, | ||
invalidationPolicies: {...} | ||
}); | ||
``` | ||
## Specification | ||
Normalized collections introduce 4 new APIs: | ||
1. `useFragmentWhere`: A new React hook for filtering a collection of entities by type | ||
2. `cache.readReferenceWhere`: A cache API that returns a list of references in the cache for a particular type and filter | ||
3. `cache.readFragmentWhere`: The collection filter equivalent of the existing cache.readFragment API | ||
4. `cache.watchFragmentWhere`: The collection filter equivalent of the existing cache.watchFragment API | ||
## useFragmentWhere | ||
The `useFragmentWhere` API allows us to query for a filtered collection of entities by type. It takes two arguments, a GraphQL fragment for the fields to read from the type and an object of all the fields to filter by. | ||
### Example Usage | ||
Now our client can filter all entites of a particular type in the cache like `Employee` in one operation without having to write any type policies. | ||
```js | ||
import { useFragmentWhere } from '@nerdwallet/apollo-cache-policies'; | ||
const { data } = useFragmentWhere( | ||
gql` | ||
fragment EmployeesByTeam on Employee { | ||
id | ||
name | ||
} | ||
`, | ||
{ | ||
filter: { team: 'Banking' } | ||
} | ||
) | ||
``` | ||
If we just want to retrieve all entities in the cache for a particular type, we can omit the filter altogether: | ||
```js | ||
import { useFragmentWhere } from '@nerdwallet/apollo-cache-policies'; | ||
const { data } = useFragmentWhere( | ||
gql` | ||
fragment AllEmployees on Employee { | ||
id | ||
name | ||
} | ||
` | ||
) | ||
``` | ||
The `useFragmentWhere` API will automatically update the component just like `useQuery` when the employees that match the filter change, including when a new employee that matches the filter criteria is added to the cache. | ||
> Note: `useFragmentWhere` subscribes to data changes based on the fragment name you provide, so to return different data from different calls to the API you will want to use different fragment names. | ||
## Cache.readReferenceWhere | ||
Normalized collections can be accessed in type policies using the new `cache.readReferenceWhere` API. `readReferenceWhere` will return a list of references for a given type and filter. | ||
### Example Usage | ||
```js | ||
const cache = new InMemoryCache({ | ||
typePolicies: { | ||
Query: { | ||
fields: { | ||
readBankingTeam: { | ||
read(_existingBankingTeam, { cache }) { | ||
return cache.readReferenceWhere<Employee>( | ||
{ | ||
__typename: 'Employee', | ||
filter: { | ||
team: 'Banking', | ||
}, | ||
} | ||
); | ||
} | ||
}, | ||
}, | ||
}, | ||
}, | ||
}); | ||
``` | ||
In this example, we use the `readReferenceWhere` API to construct a type policy that returns all entities of the `Employee` type in the cache with a field `team` matching the value `Banking`. Any number of fields can be used as filters and queries for this type policy will automatically update whenever an employee entity is added, created removed from the cache. | ||
</details> | ||
<details> | ||
<summary> | ||
Cached Reactive Variables | ||
</summary> | ||
<br> | ||
## Summary | ||
Reactive variables are a powerful and lightweight API for managing local state with Apollo. In cases where client state should be persisted across sessions, it would be helpful to be able to persist reactive variables as well. | ||
Cached reactive variables work the same as regular ones, with the additional function of writing their current value to the cache. Applications still need to set up their own cache persistence using tools like [Apollo Cache Persist](https://github.com/apollographql/apollo-cache-persist). Once cache persistence is in place, cached reactive variables will be rehydrated on new sessions with a runtime value from the cache. | ||
## Example Usage | ||
The only difference in the API when working with cached reactive variables is that a unique ID must be specified for caching. They can then be initialized with a default value, read and written to using the same APIs | ||
as other reactive variables. | ||
```javascript | ||
import { makeCachedVar } from '@nerdwallet/apollo-cache-policies'; | ||
const rv = makeCachedVar('identifier', false); | ||
rv(true); | ||
console.log(rv()); // true | ||
``` | ||
</details> |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
334407
2480