diff --git a/Writerside/doc.tree b/Writerside/doc.tree
index 088a405c..58769efe 100644
--- a/Writerside/doc.tree
+++ b/Writerside/doc.tree
@@ -19,6 +19,7 @@
+
diff --git a/Writerside/topics/compiler-plugin.md b/Writerside/topics/compiler-plugin.md
index 0184c588..9e1c0c3d 100644
--- a/Writerside/topics/compiler-plugin.md
+++ b/Writerside/topics/compiler-plugin.md
@@ -59,8 +59,9 @@ object cache {
}
```
+This map is passed to the `TypePolicyCacheKeyGenerator` when calling [the `cache()` extension](#cache-extension-function).
-Pass this map to the `TypePolicyCacheKeyGenerator` when configuring the cache:
+If you need more control over the configuration, use the `normalizedCache()` extension and pass this map to the `TypePolicyCacheKeyGenerator`:
```kotlin
val apolloClient = ApolloClient.Builder()
@@ -107,7 +108,7 @@ can use the value of `__typename` as the type of the returned object, and from t
From that it can return `User:42` as the cache key for that record.
-> If your schema has ids that are unique across the service, you can pass `CacheKey.Scope.SERVICE` to the `TypePolicyCacheKeyGenerator` constructor to save space in the cache.
+> If your schema has ids that are unique across the service, you can pass `CacheKey.Scope.SERVICE` to the [`cache()`](#cache-extension-function) extension or `TypePolicyCacheKeyGenerator` constructor to save space in the cache.
>
> In that example the cache key would be `42` instead of `User:42`.
@@ -234,8 +235,21 @@ thus saving a network request if the record is already in the cache.
#### Unions and interfaces {id="field-policy-unions-and-interfaces"}
If a field returns a union or interface it is not possible to know which concrete type will be returned at runtime, and thus prefixing
-the cache key with the correct type name is not possible. Avoiding a network call is not possible here.
+the cache key with the correct type name is not possible. A network call can't be avoided here.
-However, if your schema has ids that are unique across the service, you can pass `CacheKey.Scope.SERVICE` to the `FieldPolicyCacheResolver` constructor to skip the type name in the cache key.
-Network call avoidance will still work in that case.
+However, if your schema has ids that are unique across the service, you can pass `CacheKey.Scope.SERVICE` to the [`cache()`](#cache-extension-function) extension or `FieldPolicyCacheResolver` constructor to skip the type name in the cache key.
+Network call avoidance will work in that case.
+
+## `cache()` extension function
+
+An `ApolloClient.Builder.cache()` extension function is generated by the compiler plugin, which configures the `CacheKeyGenerator`, `MetadataGenerator`, `CacheResolver`, and `RecordMerger` based
+on the type policies, connection types, and [max ages](cache-control.md) configured in the schema:
+
+```kotlin
+val apolloClient = ApolloClient.Builder()
+ // ...
+ .cache(cacheFactory = /*...*/)
+ .build()
+```
+Optionally pass a `defaultMaxAge` (infinity by default) and `keyScope` (`CacheKey.Scope.TYPE` by default).
diff --git a/Writerside/topics/migration-guide.md b/Writerside/topics/migration-guide.md
index 697267bb..8ba21089 100644
--- a/Writerside/topics/migration-guide.md
+++ b/Writerside/topics/migration-guide.md
@@ -106,7 +106,7 @@ Previously, write methods had 2 flavors:
- a `suspend` one that accepts a `publish` parameter to control whether changes should be published to watchers
- a non-suspend one (e.g. `writeOperationSync`) that doesn't publish changes
-Now only the non-suspend ones exist and don't publish. Manually call `publish()` to notify watchers of changes.
+Now only the suspend ones exist and don't publish. Manually call `publish()` to notify watchers of changes.
```kotlin
// Replace
@@ -163,6 +163,12 @@ val apolloClient = ApolloClient.Builder()
### Other changes
+- All operations are now `suspend`.
+Note that they may **suspend** or **block** the thread depending on the underlying cache
+implementation. For example, the SQL cache implementation on Android will **block** the thread while accessing the disk. As such,
+these operations **must not** run on the main thread. You can enclose them in a `withContext` block with a `Dispatchers.IO` context to ensure
+that they run on a background thread.
+
- `readFragment()` now returns a `ReadResult` (it previously returned a ``). This allows for surfacing metadata associated to the returned data, e.g. staleness.
- Records are now rooted per operation type (`QUERY_ROOT`, `MUTATION_ROOT`, `SUBSCRIPTION_ROOT`), when previously these were all at the same level, which could cause conflicts.
@@ -190,6 +196,9 @@ interface CacheResolver {
`resolveField` can also now return a `ResolvedValue` when metadata should be returned with the resolved value (e.g. staleness).
+If you wish to use the [Cache Control](cache-control.md) feature, a [`CacheControlCacheResolver`](https://apollographql.github.io/apollo-kotlin-normalized-cache/kdoc/normalized-cache/com.apollographql.cache.normalized.api/-cache-control-cache-resolver/index.html) should be used.
+You can call the generated `cache()` extension on `ApolloClient.Builder` which will use it by default if max ages are configured in the schema.
+
### `TypePolicyCacheKeyGenerator`
You can now pass the type policies to the `TypePolicyCacheKeyGenerator` constructor, and it is recommended to do so.
@@ -248,3 +257,16 @@ For consistency, the `CacheKey` type is now used instead of `String` in more API
- `ApolloCacheHeaders.EVICT_AFTER_READ` is removed. Manually call `ApolloStore.remove()` when needed instead.
- `NormalizedCache.remove(pattern: String)` is removed. Please open an issue if you need this feature back.
+
+## Other changes {id="other-changes-2"}
+
+An `ApolloClient.Builder.cache()` extension function is generated by the compiler plugin, which configures the `CacheKeyGenerator`, `MetadataGenerator`, `CacheResolver`, and `RecordMerger` based
+on the type policies, connection types, and max ages configured in the schema:
+
+```kotlin
+val apolloClient = ApolloClient.Builder()
+ // ...
+ .cache(cacheFactory = /*...*/)
+ .build()
+```
+Optionally pass a `defaultMaxAge` (infinity by default) and `keyScope` (`TYPE` by default).
diff --git a/Writerside/topics/partial-cache-reads.md b/Writerside/topics/partial-cache-reads.md
new file mode 100644
index 00000000..0ab61c96
--- /dev/null
+++ b/Writerside/topics/partial-cache-reads.md
@@ -0,0 +1,54 @@
+# Partial cache reads
+
+The cache supports partial cache reads, in a similar way to how GraphQL supports partial responses.
+This means that if some fields are missing from the cache, the cache will return the available data along with any errors for the missing fields.
+
+## With `ApolloStore`
+
+The [`ApolloStore.readOperation()`](https://apollographql.github.io/apollo-kotlin-normalized-cache/kdoc/normalized-cache/com.apollographql.cache.normalized/-apollo-store/read-operation.html)
+API returns an `ApolloResponse` that can contain partial `data` and non-empty `errors` for any missing (or stale) fields in the cache.
+
+> `ApolloResponse.cacheInfo.isCacheHit` will be false when any field is missing.
+> `ApolloResponse.cacheInfo.isStale` will be true when any field is stale.
+
+If a cache miss exception is preferred, you can use the `ApolloResponse.errorsAsException()` extension function that returns
+a response with an exception if there are any errors.
+
+## With `ApolloClient`
+
+When executing operations, the built-in fetch policies ([`FetchPolicy.CacheFirst`](https://apollographql.github.io/apollo-kotlin-normalized-cache/kdoc/normalized-cache/com.apollographql.cache.normalized/-fetch-policy/-cache-first/index.html?query=CacheFirst),
+[`FetchPolicy.CacheOnly`](https://apollographql.github.io/apollo-kotlin-normalized-cache/kdoc/normalized-cache/com.apollographql.cache.normalized/-fetch-policy/-cache-only/index.html),
+etc.) will treat cache misses as exceptions (partial results are disabled).
+
+To benefit from partial cache reads, implement your own fetch policy interceptor as shown in this example:
+
+```kotlin
+object PartialCacheOnlyInterceptor : ApolloInterceptor {
+ override fun intercept(
+ request: ApolloRequest,
+ chain: ApolloInterceptorChain
+ ): Flow> {
+ return chain.proceed(
+ request = request
+ .newBuilder()
+ // Controls where to read the data from (cache or network)
+ .fetchFromCache(true)
+ .build()
+ )
+ }
+}
+
+// ...
+
+val apolloClient = ApolloClient.Builder()
+ /*...*/
+ .serverUrl("https://example.com/graphql")
+ .fetchPolicyInterceptor(PartialCacheOnlyInterceptor)
+ .build()
+```
+
+## Error stored in the cache
+
+Errors from the server are stored in the cache, and will be returned when reading it.
+
+By default, errors don't replace existing data in the cache. You can change this behavior with [`errorsReplaceCachedValues(true)`](https://apollographql.github.io/apollo-kotlin-normalized-cache/kdoc/normalized-cache/com.apollographql.cache.normalized/errors-replace-cached-values.html?query=fun%20%3CT%3E%20MutableExecutionOptions%3CT%3E.errorsReplaceCachedValues(errorsReplaceCachedValues:%20Boolean):%20T).