Amplify APIへの移行
Amplify DataStoreはAPIカテゴリの上に構築されており、GraphQLクエリ、ミューテーション、およびサブスクリプションを使用してAWS AppSyncと相互作用します。DataStoreから移行する場合、APIカテゴリはDataStoreのリモート同期機能を複製するためのコア構成要素を提供します。このページでは、すべてのDataStore操作をAmplify APIの同等品に移行する方法について説明します。
APIレスポンスの処理
DataStoreとAPIカテゴリの主な違いはレスポンスの型です。DataStoreメソッドはモデルオブジェクトまたはリストを直接返しますが、APIメソッドはエラーを含む可能性があるGraphQLResponseラッパーを返します。
// APIクエリはList<Todo>ではなくGraphQLResponse<PaginatedResult<Todo>>を返しますfinal request = ModelQueries.list(Todo.classType);final response = await Amplify.API.query(request: request).response;
// 常にエラーを確認してくださいif (response.hasErrors) { safePrint('Errors: ${response.errors}'); return;}
// PaginatedResultからアイテムにアクセス — アイテムがnullを含む可能性がありますfinal todos = response.data?.items.nonNulls.toList() ?? [];エラー処理パターン:
| シナリオ | 検出方法 | アクション |
|---|---|---|
| ネットワークエラー | API呼び出しの周りのtry/catch | オフラインメッセージを表示、後で再試行 |
| GraphQL検証エラー | response.hasErrors == trueかつresponse.data == null | リクエストを修正またはエラーを表示 |
| 部分的成功 | response.hasErrors == trueかつresponse.data != null | データを使用、エラーをログ |
| 成功 | response.hasErrors == falseかつresponse.data != null | データを使用 |
作成と更新(保存)
DataStoreのsave()は新しいアイテムの作成と既存のアイテムの更新の両方を処理しました。APIカテゴリでは、別のModelMutations.create()およびModelMutations.update()メソッドを使用します。
前(DataStore):
// 作成または更新 — DataStoreがアイテムの存在に基づいて判断await Amplify.DataStore.save(todo);後(API)— 作成:
// 作成 — _versionは不要で、AppSyncは自動的に1に設定しますfinal todo = Todo(name: 'my first todo', description: 'todo description');final request = ModelMutations.create(todo);final response = await Amplify.API.mutate(request: request).response;
if (response.hasErrors) { safePrint('Create failed: ${response.errors}');} else { safePrint('Created: ${response.data}');}後(API)— 更新:
// 更新 — モデルインスタンスは内部的に_versionを保持します。// ConditionalCheckFailedExceptionを避けるため、常に新しくクエリされたインスタンスを使用してください。final todoWithNewName = originalTodo.copyWith(name: 'new name');final updateRequest = ModelMutations.update(todoWithNewName);final updateResponse = await Amplify.API.mutate(request: updateRequest).response;
if (updateResponse.hasErrors) { safePrint('Update failed: ${updateResponse.errors}');} else { safePrint('Updated: ${updateResponse.data}');}削除
DataStoreのdelete()メソッドはモデルインスタンスを渡すことでアイテムを削除します。APIの同等品はModelMutations.delete()を使用します。競合解決が有効な場合、削除はソフト削除です — レコードの_deletedフィールドはDynamoDBでtrueに設定されますが、レコードは物理的には削除されません。
前(DataStore):
await Amplify.DataStore.delete(todo);後(API):
final request = ModelMutations.delete(todo);final response = await Amplify.API.mutate(request: request).response;
if (response.hasErrors) { safePrint('Delete failed: ${response.errors}');} else { safePrint('Deleted: ${response.data}');}ModelMutations.deleteById()を使用してIDで削除することもできます:
final request = ModelMutations.deleteById( Todo.classType, TodoModelIdentifier(id: todoId),);final response = await Amplify.API.mutate(request: request).response;ソフト削除と_deleted
競合解決が有効な場合、削除はソフト削除です — レコードの_deletedフィールドはDynamoDBでtrueに設定されますが、レコードは物理的には削除されません。DataStoreはこれらを自動的にフィルタリングしました。APIカテゴリはそうしません。つまり、ModelQueries.list()はソフト削除されたレコードをアクティブなレコードと共に返します。
Amplify Flutterコード生成モデルはデフォルトでDartプロパティとして_deletedを公開しません。クライアント側フィルタリングを有効にするには、schema.graphqlのモデルに_deletedを追加してamplify codegen modelsを再実行してください:
type Todo @model { id: ID! name: String! description: String _deleted: Boolean}次に、すべてのリストクエリでDartでフィルタリングします:
final todos = response.data?.items.nonNulls .where((t) => t.deleted != true) .toList() ?? [];競合解決を無効化する最終移行ステップの後、ソフト削除が停止し、このフィルタリングは不要になります。
クエリ
DataStoreのquery()メソッドはクエリプレディケート、ソート、およびページベースのページネーションをサポートします。APIカテゴリはwhereパラメータを通じてクエリプレディケートをサポートしていますが、ソートとページネーションは異なる方法で処理します。
前(DataStore):
final todos = await Amplify.DataStore.query( Todo.classType, where: Todo.NAME.contains('important'), sortBy: [Todo.CREATEDAT.descending()], pagination: QueryPagination(page: 0, limit: 10),);// List<Todo>を直接返します後(API)— 単一アイテム:
final request = ModelQueries.get( Todo.classType, queriedTodo.modelIdentifier,);final response = await Amplify.API.query(request: request).response;final todo = response.data; // Todo? — nullの可能性があります後(API)— プレディケート付きリスト:
final request = ModelQueries.list( Todo.classType, where: Todo.NAME.contains('important'),);final response = await Amplify.API.query(request: request).response;final todos = response.data?.items.nonNulls .where((t) => t.deleted != true) .toList() ?? [];ソート
APIカテゴリのModelQueries.list()はsortByパラメータをサポートしていません。結果を受け取った後、クライアント側でソートを実装する必要があります:
final request = ModelQueries.list(Todo.classType);final response = await Amplify.API.query(request: request).response;final todos = response.data?.items.nonNulls .where((t) => t.deleted != true) .toList() ?? [];
// クライアント側のソート(例:作成日順、最新順)todos.sort((a, b) { final aDate = a.createdAt?.getDateTimeInUtc() ?? DateTime(0); final bDate = b.createdAt?.getDateTimeInUtc() ?? DateTime(0); return bDate.compareTo(aDate);});ページネーション
DataStoreはページベースのページネーション(QueryPagination(page: N, limit: M))を使用し、APIカテゴリはPaginatedResultのnextTokenを介してトークンベースのページネーションを使用します。すべての結果をページネーションするには:
List<Todo> allTodos = [];GraphQLRequest<PaginatedResult<Todo>> request = ModelQueries.list( Todo.classType, limit: 100,);
while (true) { final response = await Amplify.API.query(request: request).response; final page = response.data; if (page == null) break;
allTodos.addAll(page.items.nonNulls);
if (page.hasNextResult) { request = page.requestForNextResult!; } else { break; }}UIでページベースのページネーションが必要な場合は、すべてのアイテムをフェッチしてからスライスすることで、クライアント側で実装できます:
final pageSize = 10;final pageIndex = 0; // ゼロベース
// すべてをフェッチ(またはトークンベースのページネーションを使用して十分なアイテムをフェッチ)final allTodos = await _fetchAllTodos();
// 表示用にスライスfinal pageItems = allTodos.skip(pageIndex * pageSize).take(pageSize).toList();final totalPages = (allTodos.length / pageSize).ceil();監視
DataStoreのobserve()は、EventType(作成、更新、削除)を含むイベントを持つ単一ストリームを返します。APIカテゴリは3つの個別サブスクリプションストリーム — onCreate、onUpdate、onDeleteそれぞれ1つずつ使用します。
前(DataStore):
final stream = Amplify.DataStore.observe(Todo.classType);stream.listen((event) { switch (event.eventType) { case EventType.create: safePrint('Created: ${event.item}'); break; case EventType.update: safePrint('Updated: ${event.item}'); break; case EventType.delete: safePrint('Deleted: ${event.item}'); break; }});後(API)— 3つの個別サブスクリプション:
late StreamSubscription<GraphQLResponse<Todo>> _createSub;late StreamSubscription<GraphQLResponse<Todo>> _updateSub;late StreamSubscription<GraphQLResponse<Todo>> _deleteSub;
void _initSubscriptions() { // onCreate final onCreateRequest = ModelSubscriptions.onCreate(Todo.classType); _createSub = Amplify.API .subscribe(onCreateRequest, onEstablished: () => safePrint('onCreate subscription established')) .listen( (event) { safePrint('Created: ${event.data}'); }, onError: (Object e) => safePrint('onCreate error: $e'), );
// onUpdate final onUpdateRequest = ModelSubscriptions.onUpdate(Todo.classType); _updateSub = Amplify.API .subscribe(onUpdateRequest, onEstablished: () => safePrint('onUpdate subscription established')) .listen( (event) { safePrint('Updated: ${event.data}'); }, onError: (Object e) => safePrint('onUpdate error: $e'), );
// onDelete final onDeleteRequest = ModelSubscriptions.onDelete(Todo.classType); _deleteSub = Amplify.API .subscribe(onDeleteRequest, onEstablished: () => safePrint('onDelete subscription established')) .listen( (event) { safePrint('Deleted: ${event.data}'); }, onError: (Object e) => safePrint('onDelete error: $e'), );}
void dispose() { _createSub.cancel(); _updateSub.cancel(); _deleteSub.cancel(); super.dispose();}ObserveQuery
DataStoreのobserveQuery()は初期クエリをリアルタイム更新と組み合わせ、マッチするアイテムの完全なリストとisSyncedフラグを含むQuerySnapshotを返します。APIカテゴリには直接的な同等品がありません — 初期リストクエリとサブスクリプションを使用してこの動作を自分で構成する必要があります。
前(DataStore):
final stream = Amplify.DataStore.observeQuery( Todo.classType, where: Todo.STATUS.eq(TodoStatus.ACTIVE), sortBy: [Todo.CREATEDAT.descending()],);
stream.listen((QuerySnapshot<Todo> snapshot) { safePrint( 'Items: ${snapshot.items.length}, isSynced: ${snapshot.isSynced}'); setState(() { _todos = snapshot.items; });});後(API)— 初期クエリ+リアクティブ更新用サブスクリプション:
List<Todo> _todos = [];bool _isLoading = true;late StreamSubscription _createSub;late StreamSubscription _updateSub;late StreamSubscription _deleteSub;
Future<void> _initObserveQuery() async { // ステップ1:初期リストクエリを実行 await _refreshList();
// ステップ2:変更にサブスクライブし、各イベントでリフレッシュ final onCreate = ModelSubscriptions.onCreate(Todo.classType); _createSub = Amplify.API.subscribe(onCreate).listen((_) => _refreshList());
final onUpdate = ModelSubscriptions.onUpdate(Todo.classType); _updateSub = Amplify.API.subscribe(onUpdate).listen((_) => _refreshList());
final onDelete = ModelSubscriptions.onDelete(Todo.classType); _deleteSub = Amplify.API.subscribe(onDelete).listen((_) => _refreshList());}
Future<void> _refreshList() async { final request = ModelQueries.list( Todo.classType, where: Todo.STATUS.eq(TodoStatus.ACTIVE), ); final response = await Amplify.API.query(request: request).response;
if (response.hasErrors) { safePrint('Query errors: ${response.errors}'); return; }
final items = response.data?.items.nonNulls .where((t) => t.deleted != true) .toList() ?? [];
// クライアント側のソート(APIはsortByをサポートしていません) items.sort((a, b) { final aDate = a.createdAt?.getDateTimeInUtc() ?? DateTime(0); final bDate = b.createdAt?.getDateTimeInUtc() ?? DateTime(0); return bDate.compareTo(aDate); });
setState(() { _todos = items; _isLoading = false; });}
void dispose() { _createSub.cancel(); _updateSub.cancel(); _deleteSub.cancel(); super.dispose();}クイックリファレンステーブル
| DataStoreメソッド | Amplify API同等品 | 主な違い |
|---|---|---|
Amplify.DataStore.save()(作成) | Amplify.API.mutate(request: ModelMutations.create(...)) | response.hasErrorsを確認する必要があります。作成には_versionは不要です |
Amplify.DataStore.save()(更新) | Amplify.API.mutate(request: ModelMutations.update(...)) | 最初にクエリして最新の_versionを取得する必要があります。モデルはそれを内部的に保持しています |
Amplify.DataStore.delete() | Amplify.API.mutate(request: ModelMutations.delete(...)) | 最初にクエリして最新の_versionを取得する必要があります。競合解決が有効な場合、削除はソフト削除です |
Amplify.DataStore.query()(単一) | Amplify.API.query(request: ModelQueries.get(...)) | GraphQLResponseラッパーを返します |
Amplify.DataStore.query()(リスト) | Amplify.API.query(request: ModelQueries.list(...)) | トークンベースのページネーション、sortByなし。ソフト削除されたレコードを返します |
Amplify.DataStore.observe() | Amplify.API.subscribe(ModelSubscriptions.onCreate/onUpdate/onDelete(...)) | 3つの個別サブスクリプション |
Amplify.DataStore.observeQuery() | 初期リストクエリ+3つのサブスクリプション | 直接的な同等品なし。手動で構成してください |
Amplify.DataStore.clear() | 不要 | クリアするローカルDataStoreがありません |