-
Notifications
You must be signed in to change notification settings - Fork 4
Rework the Pagination doc #86
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
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
.link { | ||
border-color: inherit; | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,15 +1,21 @@ | ||
<?xml version="1.0" encoding="UTF-8"?> | ||
<!DOCTYPE instance-profile | ||
SYSTEM "https://resources.jetbrains.com/writerside/1.0/product-profile.dtd"> | ||
<!DOCTYPE instance-profile SYSTEM "https://resources.jetbrains.com/writerside/1.0/product-profile.dtd"> | ||
|
||
<instance-profile id="doc" | ||
name="apollo-kotlin-normalized-cache-incubating" | ||
start-page="welcome.md"> | ||
<instance-profile | ||
id="doc" | ||
name="apollo-kotlin-normalized-cache-incubating" | ||
start-page="welcome.md" | ||
> | ||
|
||
<toc-element toc-title="GitHub" href="https://github.com/apollographql/apollo-kotlin-normalized-cache-incubating" /> | ||
<toc-element toc-title="Kdoc" href="https://apollographql.github.io/apollo-kotlin-normalized-cache-incubating/kdoc" /> | ||
<toc-element topic="welcome.md" /> | ||
<toc-element topic="pagination-home.md"> | ||
<toc-element topic="pagination-relay-style.md" /> | ||
<toc-element topic="pagination-other.md" /> | ||
<toc-element topic="pagination-manual.md" /> | ||
</toc-element> | ||
<toc-element topic="cache-control.md" /> | ||
<toc-element topic="garbage-collection.md" /> | ||
|
||
<toc-element toc-title="GitHub" href="https://github.com/apollographql/apollo-kotlin-normalized-cache-incubating"/> | ||
<toc-element toc-title="Kdoc" href="https://apollographql.github.io/apollo-kotlin-normalized-cache-incubating/kdoc"/> | ||
<toc-element topic="welcome.md"/> | ||
<toc-element topic="pagination.md"/> | ||
<toc-element topic="cache-control.md"/> | ||
<toc-element topic="garbage-collection.md"/> | ||
</instance-profile> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,114 @@ | ||
# Pagination | ||
|
||
The normalized cache includes support for pagination, allowing you to merge pages of data into the same record field. | ||
This allows your application to watch a query for a list of items, receive updates when new pages are fetched, and update the UI with the full list. | ||
|
||
- If your schema uses [Relay-style](https://relay.dev/graphql/connections.htm) pagination, the library [supports it](pagination-relay-style.md) out of the box. | ||
- For other types of pagination, you can still use the pagination support, with more configuration needed. | ||
- If you need more control, you can also [manually handle pagination](pagination-manual.md) using the `ApolloStore` APIs. | ||
|
||
Keep reading to learn more about pagination in the context of a normalized cache. | ||
|
||
## Pagination with a normalized cache | ||
|
||
When using the normalized cache, objects are stored in records keyed by the object's id: | ||
|
||
Query: | ||
|
||
```graphql | ||
query Users { | ||
allUsers(groupId: 2) { | ||
id | ||
name | ||
} | ||
} | ||
``` | ||
|
||
Response: | ||
|
||
```json | ||
{ | ||
"data": { | ||
"allUsers": [ | ||
{ | ||
"id": 1, | ||
"name": "John Smith" | ||
}, | ||
{ | ||
"id": 2, | ||
"name": "Jane Doe" | ||
} | ||
] | ||
} | ||
} | ||
``` | ||
|
||
Normalized cache: | ||
|
||
| Cache Key | Record | | ||
|------------|---------------------------------------------------| | ||
| QUERY_ROOT | allUsers(groupId: 2): [ref(user:1), ref(user:2)] | | ||
| user:1 | id: 1, name: John Smith | | ||
| user:2 | id: 2, name: Jane Doe | | ||
|
||
The app can watch the `Users()` query and update the UI with the whole list when the data changes. | ||
|
||
However with pagination things become less obvious: | ||
|
||
Query: | ||
|
||
```graphql | ||
query UsersPage($page: Int!) { | ||
usersPage(groupId: 2, page: $page) { | ||
id | ||
name | ||
} | ||
} | ||
``` | ||
|
||
Response: | ||
|
||
```json | ||
{ | ||
"data": { | ||
"usersPage": [ | ||
{ | ||
"id": 1, | ||
"name": "John Smith" | ||
}, | ||
{ | ||
"id": 2, | ||
"name": "Jane Doe" | ||
} | ||
] | ||
} | ||
} | ||
``` | ||
|
||
Normalized cache: | ||
|
||
| Cache Key | Record | | ||
|------------|------------------------------------------------------------| | ||
| QUERY_ROOT | usersPage(groupId: 2, page: 1): [ref(user:1), ref(user:2)] | | ||
| user:1 | id: 1, name: John Smith | | ||
| user:2 | id: 2, name: Jane Doe | | ||
|
||
After fetching page 2, the cache will look like this: | ||
|
||
| Cache Key | Record | | ||
|------------|-----------------------------------------------------------------------------------------------------------------------------| | ||
| QUERY_ROOT | usersPage(groupId: 2, page: 1): [ref(user:1), ref(user:2)], <br/>usersPage(groupId: 2, page: 2): [ref(user:3), ref(user:4)] | | ||
| user:1 | id: 1, name: John Smith | | ||
| user:2 | id: 2, name: Jane Doe | | ||
| user:3 | id: 3, name: Peter Parker | | ||
| user:4 | id: 4, name: Bruce Wayne | | ||
|
||
Which query should the app watch to update the UI? | ||
|
||
Watching `UsersPage(page = 1)` would only notify changes to the first page. | ||
|
||
For the whole list to be reactive you'd need to watch the queries for each page, and update the corresponding segment of the list. While technically possible, this is cumbersome to implement. | ||
|
||
You could skip watching altogether and only update the list when scrolling to its end, but that would mean that changes to individual items would not be reflected in the list UI. | ||
|
||
What we need is having the whole list in a single field, so we can watch a single query. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
# Managing pagination manually | ||
|
||
Using the [`ApolloStore`](https://apollographql.github.io/apollo-kotlin-normalized-cache-incubating/kdoc/normalized-cache-incubating/com.apollographql.cache.normalized/-apollo-store/index.html) | ||
APIs, you can update the cache manually whenever you fetch a new page of data. | ||
|
||
Here's a general outline of how you can do this: | ||
|
||
```kotlin | ||
suspend fun fetchAndMergePage(nextPage: Int) { | ||
// 1. Get the current list from the cache | ||
val listQuery = UsersPageQuery(page = 1) | ||
val cacheResponse = apolloClient.query(listQuery).fetchPolicy(FetchPolicy.CacheOnly).execute() | ||
|
||
// 2. Fetch the next page from the network (don't update the cache yet) | ||
val networkResponse = apolloClient.query(UsersPageQuery(page = nextPage)).fetchPolicy(FetchPolicy.NetworkOnly).execute() | ||
|
||
// 3. Merge the next page with the current list | ||
val mergedList = cacheResponse.data.usersPage.items + networkResponse.data.usersPage.items | ||
val dataWithMergedList = networkResponse.data.copy( | ||
usersPage = networkResponse.data.usersPage.copy( | ||
items = mergedList | ||
) | ||
) | ||
|
||
// 4. Update the cache with the merged list | ||
val keys = apolloClient.apolloStore.writeOperation(operation = listQuery, operationData = dataWithMergedList) | ||
apolloClient.apolloStore.publish(keys) | ||
} | ||
``` | ||
|
||
Note that in this simple example, we need to remember the last fetched page, so we can know which page to fetch next. | ||
This can be stored in shared preferences for instance. However in most cases the API can return a "page info" object containing the information needed to fetch the next page, and this can be stored | ||
in the cache with the rest of the data. | ||
|
||
An example of doing this is available [here](https://github.com/apollographql/apollo-kotlin-normalized-cache-incubating/tree/main/samples/pagination/manual). |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This change is because I find the links with the default CSS to be very hard to notice.
Before/after:

