Migrate DataStore to Apollo
This page covers how to replace every Amplify.DataStore.* call with the equivalent Apollo iOS 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 iOS, you create, update, and delete data using the mutations defined in your schema.
// Create — include all required fields from your schemalet createPostInput = CreatePostInput( title: "post", status: .some(.case(.active)), rating: .some(5), content: .some("content"))
apolloClient.perform( mutation: CreatePostMutation(input: createPostInput)) { result in guard let data = try? result.get().data else { return } print("Post: \(String(describing: data.createPost?.fragments.postDetails))")}
// Update — use .some() for fields you want to changelet updatePostInput = UpdatePostInput( id: "post1", status: .some(.case(.inactive)))
apolloClient.perform( mutation: UpdatePostMutation(input: updatePostInput)) { result in guard let data = try? result.get().data else { return } print("Post updated: \(String(describing: data.updatePost?.fragments.postDetails))")}
// Deletelet deletePostInput = DeletePostInput(id: "post1")apolloClient.perform( mutation: DeletePostMutation(input: deletePostInput)) { result in guard let data = try? result.get().data else { return } print("Post deleted: \(String(describing: data.deletePost?.id))")}Query
In Apollo iOS, you query data using the queries defined in your schema.
// Single Itemlet query = GetPostQuery(id: "post1")apolloClient.fetch(query: query) { result in guard let data = try? result.get().data else { return } print("Query results: \(String(describing: data.getPost?.fragments.postDetails))")}
// List Itemslet query = GetPostsQuery()apolloClient.fetch(query: query) { result in guard let data = try? result.get().data else { return } print("List results: \(String(describing: data.listPosts?.items))")}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 |