-
Notifications
You must be signed in to change notification settings - Fork 2
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
RFC: Pagination helpers #38
Comments
And we need relayStylePagination helper function. |
We have a paginated query that returns us a list of our app items, like so…
where the The use case is for us to regularly sync our items from the server to the normalised cache, and our UI will watch the cache. It would be great if there was internal workings in the apolloClient to help us manage this. |
Hey @martinbonnin 👋 Did this get prioritised internally? Just wondering if this is something we can expect in the short/medium term? Thanks! 🙏 |
Yes, it's one of the next big thing on the todo list after apollographql/apollo-kotlin#3566. Pretty hard to commit to a date at this point but the coming months are the current goal. |
Hi 👋 We started investigating these APIs. You can read more about it in the design document. The tldr; is:
// build.gradle.kts
dependencies {
implementation("com.apollographql.apollo3:apollo-normalized-cache-incubating")
}
extend type Query @typePolicy(connectionFields: "usersConnection")
val apolloStore = ApolloStore(
normalizedCacheFactory = cacheFactory,
cacheKeyGenerator = TypePolicyCacheKeyGenerator,
metadataGenerator = ConnectionMetadataGenerator(Pagination.connectionTypes),
apolloResolver = FieldPolicyApolloResolver,
recordMerger = ConnectionRecordMerger
) Updating the cache now merges new items with the existing ones Warning The persisted database format is different so you can't use it together with the non-incubating DB
It's still the early days and too early to use in production but any feedback is warmly welcome. |
@martinbonnin Do you have plans to support the following format? type UserConnection {
pageInfo: PageInfo!
nodes: [User!]!
} Shopify and GitHub APIs have nodes in addition to edges. https://shopify.dev/docs/api/admin-graphql/2024-01/queries/customers#returns |
Hey @sonatard, this is great feedback! I've created issue apollographql/apollo-kotlin#5735 to follow-up on this. Short answer is the current system is flexible enough to manually configure it to work with queries selecting |
Hi 👋 The |
@martinbonnin I tried it right away, and it's working smoothly even with nodes! Thank you!
query StorefrontCollectionProductsPage($id: ID!, $sordKey: ProductCollectionSortKeys, $reverse: Boolean, $first: Int!, $after: String, $filters: [ProductFilter!]!, $countryCode: CountryCode!, $languageCode: LanguageCode!) @inContext(country: $countryCode, language: $languageCode) {
collection(id: $id) {
id
title
products(first: $first, sortKey: $sordKey, reverse: $reverse, after: $after, filters: $filters) {
nodes {
id
... ProductItemFragment
}
pageInfo {
endCursor
hasNextPage
}
}
}
}
val afterQuery = q.copy(after = Optional.presentIfNotNull(collection.products.pageInfo.endCursor))
- val resp = storefront.query(afterQuery).fetchPolicy(FetchPolicy.NetworkOnly).execute()
- val respData = resp.data ?: return@LaunchedEffect
- val respCollection = respData.collection ?: return@LaunchedEffect
- val newNodes = collection.products.nodes + respCollection.products.nodes
- val newData =
- respData.copy(
- collection =
- respCollection.copy(
- products = respCollection.products.copy(nodes = newNodes)
- )
- )
- val keys = storefront.apolloStore.writeOperation(q, newData)
- storefront.apolloStore.publish(keys)
+ storefront.query(afterQuery).fetchPolicy(FetchPolicy.NetworkOnly).execute()
- implementation("com.apollographql.apollo:apollo-normalized-cache:$apolloVersion")
+ val apolloNormalizedCacheIncubating = "0.0.2"
+ implementation("com.apollographql.cache:normalized-cache-incubating:$apolloNormalizedCacheIncubating") - schemaFiles.from("src/main/java/com/xxx/android/storefront.graphqls")
+ schemaFiles.from("src/main/java/com/xxx/android/storefront.graphqls", "src/main/java/com/xxx/android/connection.storefront.graphqls")
+ extend type Collection @typePolicy(connectionFields: "products")
+ extend type Collection @fieldPolicy(forField: "products" paginationArgs: "after")
.normalizedCache(
normalizedCacheFactory = memoryCacheFactory,
cacheKeyGenerator = IdCacheKeyGenerator,
- cacheResolver = IdCacheResolver,
+ metadataGenerator = ConnectionMetadataGenerator(connectionTypes = connectionTypes),
+ recordMerger = ConnectionRecordMerger |
@martinbonnin The cacheResolver has been removed from normalizedCache. How can I use the id field as the key for the cache? Or will the id field automatically be used as the cache key without any configuration? |
@sonatard Glad that it works well for you! Also you should be able to remove About the cache resolver: it's still there but the API changed a bit: |
Thanks!! I removed it.
I found this PR, but it seems it hasn't been released yet. Is there a planned release date? I created an IdCacheResolver using ApolloResolver, but in the next release, ApolloResolver will be replaced with CacheResolver.
object IdCacheResolver : ApolloResolver {
override fun resolveField(context: ResolverContext): Any? {
val id = context.field.argumentValue("id", context.variables).getOrNull()?.toString()
if (id != null) {
return CacheKey(id)
}
return DefaultApolloResolver.resolveField(context)
}
}
.normalizedCache(
normalizedCacheFactory = memoryCacheFactory,
cacheKeyGenerator = IdCacheKeyGenerator,
- cacheResolver = IdCacheResolver,
+ apolloResolver = IdCacheResolver,
+ metadataGenerator = ConnectionMetadataGenerator(connectionTypes = connectionTypes),
+ recordMerger = ConnectionRecordMerger |
Woops you are right @sonatard, sorry about the confusion, I forgot this hadn't been released yet. I just published a new release, v0.0.3, which contains the change. It should be available in a few minutes. |
@BoD Thank you for your prompt response. It turned out to be a simple diff.
object IdCacheResolver : CacheResolver {
override fun resolveField(context: ResolverContext): Any? {
val id =
context.field
.argumentValue("id", context.variables)
.getOrNull()
?.toString()
if (id != null) {
return CacheKey(id)
}
return DefaultCacheResolver.resolveField(context)
}
} .normalizedCache(
normalizedCacheFactory = memoryCacheFactory,
cacheKeyGenerator = IdCacheKeyGenerator,
cacheResolver = IdCacheResolver,
+ metadataGenerator = ConnectionMetadataGenerator(connectionTypes = connectionTypes),
+ recordMerger = ConnectionRecordMerger Everything worked perfectly. Thank you for the excellent work. I am looking forward to its official release. |
In v0.0.1, a Pagination feature has been added.
It allows to have pages of data being automatically merged in a single field of a record, which allows to watch and update the UI as the list grows.
Pleas use this ticket to give any feedback or questions regarding that feature.
The text was updated successfully, but these errors were encountered: