高度なパターン
このページでは4つの高度なトピックをカバーしています。命令型のDataStore呼び出しから宣言型のApolloフックへのReactコンポーネントの移行、複合キーとカスタム主キー、型安全な操作のためのGraphQL codegen、DataStore機能でApollo Clientに直接同等のものがないものの正直な説明です。
複合キーとカスタム主キー
Amplifyはモデルの3つのIdentifierモードをサポートしています。各モードはレコードのクエリ、更新、削除方法を変更します -- そして各モードは異なるApollo Clientの設定が必要です。
3つのIdentifierモード
| Identifierモード | Gen 1スキーマ | GraphQL Get入力 | 作成入力 |
|---|---|---|---|
| デフォルト自動生成ID | @primaryKeyディレクティブなし | getModel(id: ID!) | idはAppSyncにより自動生成 |
| カスタム単一フィールドPK | カスタムフィールドの@primaryKey(sortKeyFields: []) | getModel(id: ID!) | 作成入力でidが必須 |
| 複合PK | @primaryKey(sortKeyFields: ["field2"]) | getModel(field1: ..., field2: ...) | すべてのPKフィールドが必須 |
デフォルト(自動ID)
これはモデルで@primaryKeyを使用しない場合のデフォルトモードです。AppSyncはUUID idフィールドを自動生成します。特別な移行は必要ありません -- CRUD操作の移行ページの標準CRUD パターンが直接適用されます。
Gen 1スキーマ:
# amplify/backend/api/<your-api>/schema.graphqltype Post @model @auth(rules: [{ allow: owner }]) { id: ID! title: String! content: String status: String}カスタム単一フィールドPK
モデルがカスタム主キーフィールドを定義する場合、idは自動生成されなくなります。作成ミューテーションで明示的に提供する必要があります。
Gen 1スキーマ:
# amplify/backend/api/<your-api>/schema.graphqltype Product @model @auth(rules: [{ allow: owner }]) { id: ID! @primaryKey sku: String! name: String! price: Float}Apollo Client:
const { data } = await apolloClient.mutate({ mutation: CREATE_PRODUCT, variables: { input: { id: 'PROD-001', // 必須 -- これを提供する必要があります sku: 'SKU-12345', name: 'Widget', price: 29.99, }, },});複合PK
このモードは最も移行作業が必要です。モデルがsortKeyFieldsを持つ@primaryKeyを使用する場合、すべての主キーフィールドが必須の引数になります。
Gen 1スキーマ:
type StoreBranch @model @auth(rules: [{ allow: owner }]) { tenantId: ID! @primaryKey(sortKeyFields: ["branchName"]) branchName: String! address: String phone: String}Apollo Clientクエリとミューテーション:
// 複合キーでクエリ -- 両フィールドを別の変数としてconst { data } = await apolloClient.query({ query: GET_STORE_BRANCH, variables: { tenantId: 'tenant-123', branchName: 'Downtown' },});
// 更新 -- 入力内のすべてのPKフィールド + _versionが必須await apolloClient.mutate({ mutation: UPDATE_STORE_BRANCH, variables: { input: { tenantId: 'tenant-123', branchName: 'Downtown', address: '456 New St', _version: data.getStoreBranch._version, }, },});複合キーのキャッシュ設定(typePolicies)
import { InMemoryCache } from '@apollo/client';
const cache = new InMemoryCache({ typePolicies: { // デフォルトモデルは自動的に機能します Post: { keyFields: ['id'] }, // 複合キーモデルは明示的なkeyFieldsが必要です StoreBranch: { keyFields: ['tenantId', 'branchName'] }, // カスタム単一フィールドPK Product: { keyFields: ['sku'] }, },});keyFieldsが見つからない警告の兆候: クエリがミューテーション後に古いデータを返す、Apollo DevToolsが重複エントリを表示、cache.readQueryが存在することがわかっているレコードに対してnullを返す。
GraphQL codegenで型安全な操作を実現
前のページのCRUD例では(post: any)キャストを使用しています。このセクションではそれを排除する方法を示します。
ステップ1:GraphQL操作を生成
amplify codegenこれにより、操作を文字列定数として含むsrc/graphql/にTypeScriptファイルが生成されます。
ステップ2:gql()とTypeScript型でラップ
生成された文字列をラップする型付き操作ファイルを作成します:
完全な型付き操作.tsの例
import { gql, TypedDocumentNode } from '@apollo/client';import { getPost as getPostString, listPosts as listPostsString } from './queries';import { createPost as createPostString, updatePost as updatePostString, deletePost as deletePostString } from './mutations';
export interface Post { id: string; title: string; content: string; status: string; rating: number; createdAt: string; updatedAt: string; _version: number; _deleted: boolean | null; _lastChangedAt: number;}
export interface GetPostData { getPost: Post | null; }export interface GetPostVars { id: string; }
export interface ListPostsData { listPosts: { items: Post[]; nextToken: string | null; };}export interface ListPostsVars { filter?: Record<string, unknown>; limit?: number; nextToken?: string;}
export interface CreatePostData { createPost: Post; }export interface CreatePostVars { input: { title: string; content: string; status?: string; rating?: number; };}
export interface UpdatePostData { updatePost: Post; }export interface UpdatePostVars { input: { id: string; _version: number; title?: string; content?: string; };}
export interface DeletePostData { deletePost: Post; }export interface DeletePostVars { input: { id: string; _version: number; };}
export const GET_POST: TypedDocumentNode<GetPostData, GetPostVars> = gql(getPostString);export const LIST_POSTS: TypedDocumentNode<ListPostsData, ListPostsVars> = gql(listPostsString);export const CREATE_POST: TypedDocumentNode<CreatePostData, CreatePostVars> = gql(createPostString);export const UPDATE_POST: TypedDocumentNode<UpdatePostData, UpdatePostVars> = gql(updatePostString);export const DELETE_POST: TypedDocumentNode<DeletePostData, DeletePostVars> = gql(deletePostString);ステップ3:型安全なフックを使用
TypedDocumentNodeを使用すると、Apolloフックは自動的にデータと変数の型を推論します:
失われたもの -- 直接同等のものがない機能
Hubイベント
DataStoreはHubを介して9つの個別のイベントをディスパッチしました。9つのうち:
| カテゴリ | 数 | 詳細 |
|---|---|---|
| 完全に置き換え | 0 | 直接のApollo同等物はありません |
| 部分的に置き換え | 2 | networkStatus(ブラウザAPIを使用)、subscriptionsEstablished(サブスクリプションコールバックを監視) |
| 同等物なし | 7 | syncQueriesStarted、syncQueriesReady、modelSynced、outboxMutationEnqueued、outboxMutationProcessed、outboxStatus、storageSubscribed |
同等物がない7つはシンクエンジン動作を説明し、Apollo Clientはシンクエンジンを持ちません。
選択的シンク(syncExpressions)
DataStoreのsyncExpressionsにより、サーバーからローカルストアに同期するレコードをフィルタリングできました。Apollo Clientには同等物がありません。
ライフサイクルメソッド
| メソッド | Apollo同等物 | 評価 |
|---|---|---|
DataStore.start() | なし(Apolloはオンデマンドで問い合わせ) | なし |
DataStore.stop() | 手動でアンサブスクライブ; apolloClient.stop()は進行中をキャンセル | なし |
DataStore.clear() | apolloClient.clearStore() + persistor.purge() | 部分的 |
競合ハンドラー設定
これはこのガイドで実装されています。競合はサーバー側で処理されます。評価:完全 (異なる場所、同じ機能)。
概要
| カテゴリ | 完全に置き換え | 部分的に置き換え | 同等物なし |
|---|---|---|---|
| Hubライフサイクルイベント(合計9) | 0 | 2 | 7 |
| 選択的シンク | 0 | 1 | 0 |
| ライフサイクルメソッド(合計3) | 0 | 1 | 2 |
| 競合ハンドラー | 1 | 0 | 0 |
| 合計 | 1 | 4 | 9 |
実践的なガイダンス
アプリケーションがシンク進捗インジケータを表示したり、アウトボックスステータスバッジを表示したりするようなUI状態のためにHubイベントに大きく依存している場合、追加のカスタム実装作業を計画してください。Apollo Clientに移行するほとんどのアプリケーションでは、監視するローカルシンクがないため、これらの機能は必要ありません。損失は実際ですが影響は低いです。