Skip to content

Documentation: add section about the compiler plugin and trimming + improve readme #171

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jun 4, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,17 @@

## 🚀 Apollo Kotlin Normalized Cache

This repository hosts [Apollo Kotlin](https://github.com/apollographql/apollo-kotlin)'s new normalized cache, aiming to replace the main repository version.
This repository hosts [Apollo Kotlin](https://github.com/apollographql/apollo-kotlin)'s new normalized cache, aiming to replace the main repository's version.

Compared to the previous version, this new normalized cache brings:

- [Pagination support](https://apollographql.github.io/apollo-kotlin-normalized-cache/pagination-home.html)
- [Cache control](https://apollographql.github.io/apollo-kotlin-normalized-cache/cache-control.html) (a.k.a. Time to live or expiration)
- [Garbage collection](https://apollographql.github.io/apollo-kotlin-normalized-cache/garbage-collection.html), and [trimming](https://apollographql.github.io/apollo-kotlin-normalized-cache/trimming.html)
- Partial results from the cache
- API simplifications
- Key scope support
- SQL cache improved performance
Comment on lines +17 to +27
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you're fine with maintaining 2 sources of truth, this is fine. Else, I'm okay with a simple README that just redirects to the main documentation site.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's not too much trouble for now :)


## 📚 Documentation

Expand Down
2 changes: 2 additions & 0 deletions Writerside/doc.tree
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,7 @@
</toc-element>
<toc-element topic="cache-control.md" />
<toc-element topic="garbage-collection.md" />
<toc-element topic="trimming.md" />
<toc-element topic="compiler-plugin.md" />

</instance-profile>
14 changes: 0 additions & 14 deletions Writerside/topics/cache-control.md
Original file line number Diff line number Diff line change
Expand Up @@ -102,20 +102,6 @@ extend type Book @cacheControlField(name: "cachedTitle", maxAge: 30)
extend type Reader @cacheControlField(name: "book", inheritMaxAge: true)
```

Then configure the Cache compiler plugin in your `build.gradle.kts`:

```kotlin
apollo {
service("service") {
packageName.set(/*...*/)

plugin("com.apollographql.cache:normalized-cache-apollo-compiler-plugin:%latest_version%") {
argument("packageName", packageName.get())
}
}
}
```

This will generate a map in `yourpackage.cache.Cache.maxAges`, that you can pass to the `SchemaCoordinatesMaxAgeProvider`:

```kotlin
Expand Down
241 changes: 241 additions & 0 deletions Writerside/topics/compiler-plugin.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,241 @@
# Compiler plugin

When setting up the Normalized Cache in your project, you need to configure the compiler plugin:

```kotlin
// build.gradle.kts
apollo {
service("service") {
// ...

// Add this
plugin("com.apollographql.cache:normalized-cache-apollo-compiler-plugin:%latest_version%") {
argument("packageName", packageName.get())
}
}
}
```

This plugin generates some code to support the Normalized Cache features, such as declarative cache IDs, pagination and cache control.

## Declarative cache IDs (`@typePolicy`)

You can refer to the [declarative cache IDs documentation](https://www.apollographql.com/docs/kotlin/caching/declarative-ids)
for a general overview of this feature.

Here are some additional details of what the compiler plugin does to support it.

Let's consider this schema for example:

```graphql
# schema.graphqls
type Query {
user(id: ID!): User
}

type User {
id: ID!
email: String!
name: String!
}
```

```graphql
# extra.graphqls
extend type User @typePolicy(keyFields: "id")
```

### Generation of `typePolicies`

A map of type names to `TypePolicy` instances is generated in a `Cache` object.

In the example above, the generated code will look like this:

```kotlin
object cache {
val typePolicies: Map<String, TypePolicy> = mapOf(
"User" to TypePolicy(keyFields = setOf("id"))
)
}

```

Pass this map to the `TypePolicyCacheKeyGenerator` when configuring the cache:

```kotlin
val apolloClient = ApolloClient.Builder()
// ...
.normalizedCache(
// ...
cacheKeyGenerator = TypePolicyCacheKeyGenerator(Cache.typePolicies)
)
.build()
```

### Addition of key fields and `__typename` to selections

The compiler automatically adds the key fields declared with `@typePolicy` to the selections that return that type.
This is to ensure that a `CacheKey` can be generated for the record.

When you query for `User`, e.g.:

```graphql
# operations.graphql
query User {
user(id: "1") {
email
name
}
}
```

The compiler plugin will automatically add the `id` and `__typename` fields to the selection set, resulting in:

```graphql
query User {
user(id: "1") {
__typename # Added by the compiler plugin
email
name
id # Added by the compiler plugin
}
}
```

Now, [TypePolicyCacheKeyGenerator](https://apollographql.github.io/apollo-kotlin-normalized-cache/kdoc/normalized-cache/com.apollographql.cache.normalized.api/-type-policy-cache-key-generator.html?query=fun%20TypePolicyCacheKeyGenerator(typePolicies:%20Map%3CString,%20TypePolicy%3E,%20keyScope:%20CacheKey.Scope%20=%20CacheKey.Scope.TYPE):%20CacheKeyGenerator)
can use the value of `__typename` as the type of the returned object, and from that see that there is one key field, `id`, for that type.

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.
>
> In that example the cache key would be `42` instead of `User:42`.

#### Unions and interfaces

Let's consider this example:

```graphql
# schema.graphqls
type Query {
search(text: String!): [SearchResult!]!
}

type Product {
shopId: String!
productId: String!
description: String!
}

type Book {
isbn: ID!
title: String!
}

union SearchResult = User | Post
```

```graphql
# extra.graphqls
extend type Product @typePolicy(keyFields: "shopId productId")
extend type Book @typePolicy(keyFields: "isbn")
```

```graphql
# operations.graphql
query Search($text: String!) {
search(text: $text) {
... on Book {
title
}
}
}
```

The plugin needs to add the key fields of all possible types of `SearchResult`, like so:

```graphql
query Search($text: String!) {
search(text: $text) {
__typename # Added by the compiler plugin
... on Book {
title
}
# Added by the compiler plugin
... on Book {
isbn
}
... on Product {
shopId
productId
}
}
}
```

The principle is the same with interfaces, for instance:

```graphql
# schema.graphqls
type Query {
search(text: String!): [SearchResult!]!
}

interface SearchResult {
summary: String!
}

type Product implements SearchResult {
summary: String!
shopId: String!
productId: String!
}

type Book implements SearchResult {
summary: String!
isbn: ID!
title: String!
}
```

The modified query would look the same as above, with the key fields of `Product` and `Book` added to the selection set.

> If key fields are defined on the interface itself, they only need to be added once, instead of once per possible type.

## Resolving to cache keys (`@fieldPolicy`)

When a field returns a type that has key fields, and takes arguments that correspond to these keys, you can use the `@fieldPolicy` directive.

For instance,

```graphql
# schema.graphqls
type Query {
user(id: ID!): User
}

type User {
id: ID!
email: String!
name: String!
}
```

```graphql
# extra.graphqls
extend type User @typePolicy(keyFields: "id")

extend type Query @fieldPolicy(forField: "user", keyArgs: "id")
```

From this, when selecting e.g. `user(id: 42)` the `FieldPolicyCacheResolver` knows to return `User:42` as a `CacheKey`,
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.

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.

15 changes: 9 additions & 6 deletions Writerside/topics/migration-guide.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
# Migration guide

The Apollo Kotlin Normalized Cache used to be part of the [Apollo Kotlin main repository](https://github.com/apollographql/apollo-kotlin).
It is now hosted in this dedicated repository and published at its own cadence and versioning scheme.
The Normalized Cache is now hosted in this dedicated repository and published at its own cadence and versioning scheme.

This guide highlights the main differences between this library and the "classic" version, and how to migrate to it.
The Normalized Cache in the [Apollo Kotlin main repository](https://github.com/apollographql/apollo-kotlin) will not receive new features - they
are added here instead. In the future, the main repository Normalized Cache will be deprecated and then removed.

This guide highlights the main differences between this library and the main repository version, and how to migrate from it.

## Artifacts and packages

Expand Down Expand Up @@ -38,11 +40,9 @@ import com.apollographql.apollo.cache.normalized.api.MemoryCacheFactory
import com.apollographql.cache.normalized.memory.MemoryCacheFactory
```

In most cases, this will be enough to migrate your project, but there were a few renames and API breaking changes. Read on for more details.

## Compiler plugin

Configure the compiler plugin in your `build.gradle.kts` file:
Configure the [compiler plugin](compiler-plugin.md) in your `build.gradle.kts` file:

```kotlin
apollo {
Expand All @@ -57,6 +57,9 @@ apollo {
}
```

In most cases, updating the coordinates/imports and adding the compiler plugin will be enough to migrate your project.
But there were also a few renames and API breaking changes - read on for more details.

## Database schema

The SQLite cache now uses a different schema.
Expand Down
16 changes: 16 additions & 0 deletions Writerside/topics/trimming.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Trimming the cache

By default, if you don't use [cache control](cache-control.md) and [garbage collection](garbage-collection.md),
the cache will grow indefinitely as more data is written to it.

To prevent this, a few APIs are available:

- [`ApolloStore.clearAll()`](https://apollographql.github.io/apollo-kotlin-normalized-cache/kdoc/normalized-cache/com.apollographql.cache.normalized/-apollo-store/index.html#-1013497887%2FFunctions%2F-1172623753):
clear the entire cache.
- [`ApolloStore.remove()`](https://apollographql.github.io/apollo-kotlin-normalized-cache/kdoc/normalized-cache/com.apollographql.cache.normalized/-apollo-store/index.html#-1351099158%2FFunctions%2F-1172623753):
remove specific records.
- [`ApolloStore.removeOperation()`](https://apollographql.github.io/apollo-kotlin-normalized-cache/kdoc/normalized-cache/com.apollographql.cache.normalized/remove-operation.html?query=fun%20%3CD%20:%20Operation.Data%3E%20ApolloStore.removeOperation(operation:%20Operation%3CD%3E,%20data:%20D,%20cacheHeaders:%20CacheHeaders%20=%20CacheHeaders.NONE):%20Set%3CString%3E) and [
`ApolloStore.removeFragment()`](https://apollographql.github.io/apollo-kotlin-normalized-cache/kdoc/normalized-cache/com.apollographql.cache.normalized/remove-fragment.html?query=fun%20%3CD%20:%20Fragment.Data%3E%20ApolloStore.removeFragment(fragment:%20Fragment%3CD%3E,%20cacheKey:%20CacheKey,%20data:%20D,%20cacheHeaders:%20CacheHeaders%20=%20CacheHeaders.NONE):%20Set%3CString%3E):
remove the records associated to specific operations or fragments.
- [`ApolloStore.trim()`](https://apollographql.github.io/apollo-kotlin-normalized-cache/kdoc/normalized-cache/com.apollographql.cache.normalized/-cache-manager/trim.html):
trim the cache by a specified amount (by default 10%) if it exceeds a certain size. The oldest (according to their updated date) records are removed.
36 changes: 32 additions & 4 deletions Writerside/topics/welcome.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,20 @@ This is [Apollo Kotlin](https://github.com/apollographql/apollo-kotlin)'s Normal

For an introduction please read the Normalized Cache [documentation](https://www.apollographql.com/docs/kotlin/caching/normalized-cache).

Note: the Normalized Cache used to be part of the [Apollo Kotlin main repository](https://github.com/apollographql/apollo-kotlin).
It is now hosted in this dedicated repository and published at its own cadence and versioning scheme.
The Normalized Cache is now hosted in this dedicated repository and published at its own cadence and versioning scheme.

Compared to the previous version, this library brings:

- [Pagination support](pagination-home.md)
- [](cache-control.md) (a.k.a. Time to live or expiration)
- [](garbage-collection.md), and [trimming](trimming.md)
- Partial results from the cache
- API simplifications
- Key scope support
- SQL cache improved performance

The Normalized Cache in the [Apollo Kotlin main repository](https://github.com/apollographql/apollo-kotlin) will not receive new features - they
are added here instead. In the future, the main repository Normalized Cache will be deprecated and then removed.

## Use in your project

Expand All @@ -14,7 +26,7 @@ It is now hosted in this dedicated repository and published at its own cadence a

{style="warning"}

Add the dependencies to your project.
1. Add the dependencies to your project

```kotlin
// build.gradle.kts
Expand All @@ -27,4 +39,20 @@ dependencies {
}
```

If you were using the classic Normalized Cache before, please consult the [migration guide](migration-guide.md).
2. Configure the [compiler plugin](compiler-plugin.md)

```kotlin
// build.gradle.kts
apollo {
service("service") {
// ...

// Add this
plugin("com.apollographql.cache:normalized-cache-apollo-compiler-plugin:%latest_version%") {
argument("packageName", packageName.get())
}
}
}
```

If you're already using the main repository Normalized Cache, please consult the [migration guide](migration-guide.md).