mcpField
G
graphql-schema

graphql-schema

github.com/chriswiles開発6.1k74 回インストール43 週間前に更新
リポジトリ

graphql-schema について

Apollo を使う開発者向けの GraphQL 実装スキルです。.gql ファイルでの操作定義から型付きフックの生成までを扱い、ミューテーションのエラー処理、状態表示、キャッシュ、楽観的更新も説明します。

主な機能

  1. 01.gql ファイルでクエリとミューテーションを定義
  2. 02コード生成で型とフックを作成
  3. 03ミューテーションのエラーと UI 状態を処理
  4. 04フェッチポリシーと楽観的更新を利用

活用シーン

  1. 01型付き GraphQL クエリの実装
  2. 02状態表示を備えたミューテーションの構築
  3. 03フィールドフラグメントの再利用とキャッシュ設定

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/chriswiles/claude-code-showcase --skill graphql-schema

インストール後、Claude Code で/graphql-schemaを入力して読み込まれたか確認します。

詳細

GraphQL スキーマのパターン

基本ルール

  1. gql リテラルをインラインに記述しないでください — .gql ファイルを作成します
  2. .gql ファイルの作成・変更後は必ずコード生成を実行してください
  3. ミューテーションには必ず onError ハンドラーを追加してください
  4. 生成されたフックを使用してください — 生の 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 要件

重要:すべてのミューテーションのトリガーは、必ず次の要件を満たしてください。

  1. ミューテーション中は無効にする — ダブルクリックを防止します
  2. 読み込み状態を表示する — 視覚的なフィードバックを提供します
  3. onError ハンドラーを設定する — 失敗したことがユーザーに伝わります
  4. 成功時のフィードバックを表示する — 成功したことがユーザーに伝わります
// 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:ミューテーションの送信パターン

コメント