Migrate DataStore to Apollo
This page covers how to replace every Amplify.DataStore.* call with the equivalent Apollo Kotlin operation. The examples below use the schema and GraphQL operations created in the Schema and GraphQL operations page.
Create, update, and delete (save/delete)
In Apollo Kotlin, you create, update, and delete data using the mutations defined in your schema.
// Create — include all required fields from your schemaval input = CreatePostInput( title = title, content = content, rating = Optional.present(rating), status = status)val response = apolloClient.mutation(CreatePostMutation(input)).execute()
// Update — use Optional.present() for fields you want to change.// Pass _version from the last query/mutation response for conflict resolution.val input = UpdatePostInput( id = post.id, status = Optional.present(PostStatus.ACTIVE), _version = Optional.present(post._version))val response = apolloClient.mutation(UpdatePostMutation(input)).execute()
// Delete — also requires _version for conflict resolutionval input = DeletePostInput( id = post.id, _version = Optional.present(post._version))val response = apolloClient.mutation(DeletePostMutation(input)).execute()Query
In Apollo Kotlin, you query data using the queries defined in your schema.
// Single itemval query = GetPostQuery(id = id)val response = apolloClient.query(query).execute()
// List itemsval response = apolloClient.query(GetPostsQuery()).execute()Filtering
DataStore supported predicate queries (for example, Post.RATING.gt(3)) which were executed locally. With Apollo, you have two options:
- Server-side filtering using AppSync's
ModelPostFilterInput. Update yourGetPostsquery to accept a filter:
query GetPosts($filter: ModelPostFilterInput, $nextToken: String) { listPosts(filter: $filter, nextToken: $nextToken) { items { ... PostDetails } nextToken }}Then pass the filter in Kotlin:
val filter = ModelPostFilterInput( rating = Optional.present(ModelIntInput(gt = Optional.present(3))))val response = apolloClient.query( GetPostsQuery(filter = Optional.present(filter))).execute()- Client-side filtering using Kotlin collection operations on the query results:
val allPosts = response.data?.listPosts?.items ?.mapNotNull { it?.postDetails } ?: emptyList()val filtered = allPosts.filter { (it.rating ?: 0) > 3 }Sorting
DataStore's QuerySortBy does not have a direct Apollo equivalent for the default listPosts query. Apply sorting client-side:
val sorted = posts.sortedByDescending { it.createdAt?.toString() ?: "" }Pagination
DataStore supported offset-based pagination (Page.startingAt(n).withLimit(5)). AppSync uses cursor-based pagination with nextToken. You can either paginate through all results using nextToken or apply client-side offset pagination with .drop() and .take():
val page = 0val pageSize = 5val pagedPosts = allPosts.drop(page * pageSize).take(pageSize)Observe
In Apollo Kotlin, you observe data using the subscriptions defined in your schema. Apollo Kotlin exposes subscriptions as a Kotlin Flow via the built-in toFlow() method:
apolloClient.subscription(OnCreateSubscription()).toFlow().collect { response -> // Handle created post}ObserveQuery
DataStore's observeQuery allows you to submit a query and then receive updates when the results change. To replicate this behavior with Apollo Kotlin, you use a normalized cache and the watch function.
To set this up, you need to:
- Add the normalized cache dependency to your
build.gradle.kts:
implementation("com.apollographql.apollo:apollo-normalized-cache:4.1.0")// For SQLite-backed persistent cache:implementation("com.apollographql.apollo:apollo-normalized-cache-sqlite:4.1.0")- Configure the cache when building your Apollo client:
val cacheFactory = MemoryCacheFactory(maxSizeBytes = 10 * 1024 * 1024)// Or for SQLite: SqlNormalizedCacheFactory(context, "apollo_cache.db")
val apolloClient = ApolloClient.Builder() .serverUrl(endpoint.serverUrl.toString()) .addHttpInterceptor(AppSyncInterceptor(authorizer)) .normalizedCache(cacheFactory) .build()- Watch the query to receive updates:
val query = GetPostQuery(id = id)apolloClient.query(query).watch().collect { post -> // This re-emits whenever the cached data for this query changes}Quick reference table
| DataStore Method | Apollo Kotlin Equivalent | Key Difference |
|---|---|---|
Amplify.DataStore.save() (create) | apolloClient.mutation(CreatePostMutation(input)).execute() | No _version needed for creates |
Amplify.DataStore.save() (update) | apolloClient.mutation(UpdatePostMutation(input)).execute() | Must pass _version from last query |
Amplify.DataStore.delete() | apolloClient.mutation(DeletePostMutation(input)).execute() | Must pass _version from last query |
Amplify.DataStore.query() (single) | apolloClient.query(GetPostQuery(id)).execute() | Returns null if not found |
Amplify.DataStore.query() (list) | apolloClient.query(GetPostsQuery()).execute() | Must filter _deleted records |
Amplify.DataStore.observe() | apolloClient.subscription(...).toFlow().collect {} | Separate subscription per event type |
Amplify.DataStore.observeQuery() | apolloClient.query(...).watch().collect {} | Requires normalized cache setup |
Amplify.DataStore.clear() | No longer needed | No local DataStore to clear |