graphql-schema について
Apollo を使う開発者向けの GraphQL 実装スキルです。.gql ファイルでの操作定義から型付きフックの生成までを扱い、ミューテーションのエラー処理、状態表示、キャッシュ、楽観的更新も説明します。
主な機能
- 01.gql ファイルでクエリとミューテーションを定義
- 02コード生成で型とフックを作成
- 03ミューテーションのエラーと UI 状態を処理
- 04フェッチポリシーと楽観的更新を利用
活用シーン
- 01型付き GraphQL クエリの実装
- 02状態表示を備えたミューテーションの構築
- 03フィールドフラグメントの再利用とキャッシュ設定
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ chriswiles/ claude-code-showcase --skill graphql-schemaインストール後、Claude Code で/graphql-schemaを入力して読み込まれたか確認します。
詳細
GraphQL スキーマのパターン
基本ルール
gqlリテラルをインラインに記述しないでください —.gqlファイルを作成します.gqlファイルの作成・変更後は必ずコード生成を実行してください- ミューテーションには必ず
onErrorハンドラーを追加してください - 生成されたフックを使用してください — 生の Apollo フックは記述しないでください
ファイル構成
src/
├── components/
│ └── ItemList/
│ ├── ItemList.tsx
│ ├── GetItems.gql # Query definition
│ └── GetItems.generated.ts # Auto-generated (don't edit)
└── graphql/
└── mutations/
└── CreateItem.gql # Shared mutations
クエリの作成
ステップ 1:.gql ファイルを作成する
# src/components/ItemList/GetItems.gql
query GetItems($limit: Int, $offset: Int) {
items(limit: $limit, offset: $offset) {
id
name
description
createdAt
}
}
ステップ 2:コード生成を実行する
npm run gql:typegen
ステップ 3:生成されたフックをインポートして使用する
import { useGetItemsQuery } from './GetItems.generated';
const ItemList = () => {
const { data, loading, error, refetch } = useGetItemsQuery({
variables: { limit: 20, offset: 0 },
});
if (error) return <ErrorState error={error} onRetry={refetch} />;
if (loading && !data) return <LoadingSkeleton />;
if (!data?.items.length) return <EmptyState />;
return <List items={data.items} />;
};
ミューテーションの作成
ステップ 1:.gql ファイルを作成する
# src/graphql/mutations/CreateItem.gql
mutation CreateItem($input: CreateItemInput!) {
createItem(input: $input) {
id
name
description
}
}
ステップ 2:コード生成を実行する
npm run gql:typegen
ステップ 3:必須のエラーハンドリングを実装して使用する
import { useCreateItemMutation } from 'graphql/mutations/CreateItem.generated';
const CreateItemForm = () => {
const [createItem, { loading }] = useCreateItemMutation({
// Success handling
onCompleted: (data) => {
toast.success({ title: 'Item created' });
navigation.goBack();
},
// ERROR HANDLING IS REQUIRED
onError: (error) => {
console.error('createItem failed:', error);
toast.error({ title: 'Failed to create item' });
},
// Cache update
update: (cache, { data }) => {
if (data?.createItem) {
cache.modify({
fields: {
items: (existing = []) => [...existing, data.createItem],
},
});
}
},
});
return (
<Button
onPress={() => createItem({ variables: { input: formValues } })}
isDisabled={!isValid || loading}
isLoading={loading}
>
Create
</Button>
);
};
ミューテーションの UI 要件
重要:すべてのミューテーションのトリガーは、必ず次の要件を満たしてください。
- ミューテーション中は無効にする — ダブルクリックを防止します
- 読み込み状態を表示する — 視覚的なフィードバックを提供します
onErrorハンドラーを設定する — 失敗したことがユーザーに伝わります- 成功時のフィードバックを表示する — 成功したことがユーザーに伝わります
// CORRECT - Complete mutation pattern
const [submit, { loading }] = useSubmitMutation({
onError: (error) => {
console.error('submit failed:', error);
toast.error({ title: 'Save failed' });
},
onCompleted: () => {
toast.success({ title: 'Saved' });
},
});
<Button
onPress={handleSubmit}
isDisabled={!isValid || loading}
isLoading={loading}
>
Submit
</Button>
クエリのオプション
フェッチポリシー
| ポリシー | 使用する場面 |
|---|---|
cache-first | データがほとんど変更されない場合 |
cache-and-network | すばやさと最新データの両方が必要な場合(デフォルト) |
network-only | 常に最新データが必要な場合 |
no-cache | キャッシュを一切使用しない場合(まれ) |
よく使うオプション
useGetItemsQuery({
variables: { id: itemId },
// Fetch strategy
fetchPolicy: 'cache-and-network',
// Re-render on network status changes
notifyOnNetworkStatusChange: true,
// Skip if condition not met
skip: !itemId,
// Poll for updates
pollInterval: 30000,
});
楽観的更新
UI に即座にフィードバックを表示するには:
const [toggleFavorite] = useToggleFavoriteMutation({
optimisticResponse: {
toggleFavorite: {
__typename: 'Item',
id: itemId,
isFavorite: !currentState,
},
},
onError: (error) => {
// Rollback happens automatically
console.error('toggleFavorite failed:', error);
toast.error({ title: 'Failed to update' });
},
});
楽観的更新を使用しない場合
- バリデーションに失敗する可能性のある操作
- サーバーが値を生成する操作
- 破壊的な操作(削除)
- 他のユーザーに影響する操作
フラグメント
フィールド選択を再利用するには:
# src/graphql/fragments/ItemFields.gql
fragment ItemFields on Item {
id
name
description
createdAt
updatedAt
}
クエリで使用します:
query GetItems {
items {
...ItemFields
}
}
アンチパターン
// WRONG - Inline gql
const GET_ITEMS = gql`
query GetItems { items { id } }
`;
// CORRECT - Use .gql file + generated hook
import { useGetItemsQuery } from './GetItems.generated';
// WRONG - No error handler
const [mutate] = useMutation(MUTATION);
// CORRECT - Always handle errors
const [mutate] = useMutation(MUTATION, {
onError: (error) => {
console.error('mutation failed:', error);
toast.error({ title: 'Operation failed' });
},
});
// WRONG - Button not disabled during mutation
<Button onPress={submit}>Submit</Button>
// CORRECT - Disabled and loading
<Button onPress={submit} isDisabled={loading} isLoading={loading}>
Submit
</Button>
コード生成コマンド
# Generate types from .gql files
npm run gql:typegen
# Download schema + generate types
npm run sync-types
他のスキルとの連携
- react-ui-patterns:クエリの読み込み・エラー・空状態
- testing-patterns:テストで生成されたフックをモックする
- formik-patterns:ミューテーションの送信パターン
コメント
Markdown 対応