Name:
interface
Value:
Amplify has re-imagined the way frontend developers build fullstack applications. Develop and deploy without the hassle.

Page updated May 1, 2026

LegacyYou are viewing Gen 1 documentation. Switch to the latest Gen 2 docs →

Migrate from DataStore

Understanding DataStore

AWS Amplify DataStore provided a local-first data layer that automatically synchronized data between your app and the cloud. When you used DataStore, you got several powerful capabilities without writing any synchronization logic yourself: a local database that persisted data across sessions, automatic bidirectional sync with your AppSync backend, built-in conflict resolution using version tracking, full offline support with mutation queuing and replay, and real-time updates through observe() and observeQuery().

DataStore abstracted away the complexity of GraphQL operations, network state management, and data consistency. You worked with simple save, query, and delete methods on local models, and DataStore handled everything else behind the scenes.

This guide shows you how to use Apollo Client for queries, mutations, and caching, combined with the Amplify library's built-in subscription support for real-time updates. Depending on how much of DataStore's feature set your app actually uses, you may find the migration simpler than expected.

Once frontend clients have been migrated off DataStore, disable conflict resolution on the backend to remove the sync infrastructure, simplify mutations (no more _version tracking), and switch from soft deletes to hard deletes.

What this guide covers

This guide walks you through the migration path from DataStore to Apollo Client. You will set up Apollo Client with your AppSync endpoint, migrate all CRUD operations and relationships, and optionally add persistent caching and optimistic updates.

Quick comparison: before and after

Here is a quick look at how common DataStore operations translate to Apollo Client:

DataStore OperationApollo Client Equivalent
DataStore.save(new Post({...}))apolloClient.mutate({ mutation: CREATE_POST, variables: { input: {...} } })
DataStore.query(Post)apolloClient.query({ query: LIST_POSTS })
DataStore.query(Post, id)apolloClient.query({ query: GET_POST, variables: { id } })
DataStore.delete(post)apolloClient.mutate({ mutation: DELETE_POST, variables: { input: { id, _version } } })
DataStore.observe(Post)amplifyClient.graphql({ query: onCreatePost }).subscribe(...)
DataStore.observeQuery(Post)useQuery(LIST_POSTS) with subscription-triggered refetch()

Subscriptions use the Amplify library's client.graphql() rather than Apollo, because AppSync uses a custom WebSocket protocol that Amplify handles natively. Apollo Client handles all queries, mutations, and caching.

Who should use this guide

This guide is for developers who have an existing Amplify Gen 1 application that uses DataStore and want to replace DataStore with Apollo Client. It covers the frontend migration (replacing DataStore with Apollo Client) and the backend step of disabling conflict resolution once all clients have been migrated. After completing these steps, you can proceed to migrate your backend to Amplify Gen 2.

Gen 1 field name casing: Gen 1 backends use uppercase ID suffixes in foreign keys (e.g., postID, tagID), while Gen 2 uses lowercase (postId, tagId). Code examples in this guide use the Gen 2 convention. If your backend still uses Gen 1, adjust all field names in your GraphQL operations, sync queries, and filters to match your schema. Mismatched casing returns null silently. Verify field names in your schema.graphql or the AppSync console.

It assumes you are familiar with:

  • React and React hooks
  • Basic GraphQL concepts (queries, mutations, subscriptions)
  • Amplify configuration and the amplifyconfiguration.json (or aws-exports.js) file
  • Your app's data model and how it uses DataStore today

You do not need prior experience with Apollo Client. The guide covers Apollo Client setup from scratch.

How to use this guide

Follow the pages in order. Each step builds on the previous one.

  1. Set up Apollo Client. Install dependencies, define your GraphQL operations, configure Apollo Client with your AppSync endpoint and Cognito authentication, and set up real-time subscriptions with Amplify.

  2. Migrate CRUD operations. Replace DataStore.save(), DataStore.query(), and DataStore.delete() with Apollo Client queries and mutations. Migrate predicates, pagination, and sorting.

  3. Migrate relationships. Migrate hasMany, belongsTo, hasOne, and manyToMany relationships from DataStore's lazy-loading model to Apollo Client's GraphQL selection sets.

  4. Optional: Add local caching. Enhance your setup with persistent caching (data survives page refreshes), optimistic updates (instant UI feedback), and intelligent fetch policies. This step is optional -- skip it if your app does not need data persistence across page refreshes or instant optimistic UI.

  5. Advanced patterns. Handle composite keys, set up GraphQL codegen for type safety, migrate React components, and review DataStore features with no direct Apollo equivalent.

  6. Disable conflict resolution. Once ALL frontend clients are migrated off DataStore, remove the sync infrastructure, simplify mutations, and switch from soft deletes to hard deletes.

  7. Migrate your backend to Gen 2 (optional). After completing the frontend migration and disabling conflict resolution, you can migrate your backend from Gen 1 to Amplify Gen 2.

Model coverage: Code examples use Post and Comment models. For additional models (including join tables like PostTag), extend the same patterns.

Migration checklists

Use these checklists to plan and track your migration from DataStore to Apollo Client. Check items off as you complete them.

Pre-migration checklist

Complete these steps before writing any migration code:

  • Verify your backend is deployed and amplifyconfiguration.json (or aws-exports.js) is generated (amplify push)
  • Confirm Amplify is configured with Amplify.configure(config) running at app startup before any API calls
  • Inventory all DataStore usage in your codebase: DataStore.save(), DataStore.query(), DataStore.delete(), DataStore.observe(), DataStore.observeQuery()
  • Identify all models and relationships (hasMany, belongsTo, hasOne, manyToMany), noting which models have custom or composite primary keys
  • Write GraphQL operations for each model, including _version, _deleted, and _lastChangedAt in all fragments
  • Install Apollo Client: npm install @apollo/client@^3.14.0 graphql
  • Set up Apollo Client and verify the connection works by running a simple list query against your AppSync endpoint
  • Set up the Amplify subscription client using generateClient()

During migration checklist

Follow these steps while migrating each feature. Work through one model at a time to keep changes manageable and testable.

For each DataStore model:

  • Define a GraphQL fragment including all business fields plus _version, _deleted, and _lastChangedAt
  • Define all GraphQL operations (list, get, create, update, delete) using the fragment
  • Migrate list queries, filtering out soft-deleted records (_deleted: true) in the results
  • Migrate single-item queries
  • Migrate creates (no _version needed for creates)
  • Migrate updates (include _version from the latest query result in the mutation input)
  • Migrate deletes (include both id and _version in the mutation input)
  • Migrate observe using Amplify subscription plus the refetch pattern
  • Migrate observeQuery using useQuery combined with subscription-triggered refetch()
  • Update error handling for Apollo's error link and component-level error states
  • Test each migrated operation before moving to the next model

For predicates and filters:

  • Convert DataStore predicates to GraphQL filter objects
  • Migrate sorting to client-side .sort() or server-side @index queries
  • Migrate pagination from page-based to cursor-based (nextToken and limit)

Post-migration checklist

Verification:

  • Verify all CRUD operations work for every migrated model
  • Verify real-time updates fire for all three event types (create, update, delete) on every model
  • Verify authentication flow including sign-in, authenticated operations, and sign-out
  • Verify sign-out cleanup clears the Apollo cache
  • Verify _version handling succeeds without ConditionalCheckFailedException errors
  • Verify soft-delete filtering so deleted records no longer appear in list views

Cleanup:

  • Remove all DataStore imports from your codebase
  • Remove generated DataStore model files
  • Remove DataStore configuration calls (DataStore.configure(), DataStore.start(), DataStore.stop())
  • Remove @aws-amplify/datastore from your dependencies
  • Run the app end-to-end with a full user workflow
  • Monitor for errors post-deployment
If you added local caching

If you followed the optional Add local caching step, add these items to your migration plan:

  • Set up apollo3-cache-persist for persistent cache storage
  • Configure fetchPolicy for each query (for example, cache-and-network for lists, cache-first for detail views)
  • Implement optimistic updates for mutations using Apollo's optimisticResponse option
  • Update the sign-out flow to purge the persistent cache in addition to clearing the in-memory cache