Agent-almanac use-graphql-api
git clone https://github.com/pjt222/agent-almanac
T=$(mktemp -d) && git clone --depth=1 https://github.com/pjt222/agent-almanac "$T" && mkdir -p ~/.claude/skills && cp -r "$T/i18n/ja/skills/use-graphql-api" ~/.claude/skills/pjt222-agent-almanac-use-graphql-api-3848e6 && rm -rf "$T"
i18n/ja/skills/use-graphql-api/SKILL.mdGraphQL APIの使用
コマンドラインからGraphQLオペレーションを発見・構築・実行・連鎖させます。
使用タイミング
- GraphQLエンドポイント(GitHub、Hasura、Apolloなど)経由でデータを照会・更新するとき
- GraphQLを必要とするGitHubオペレーション(Discussions、Projects v2)を自動化するとき
- GraphQL APIから構造化データを取得するシェルスクリプトを構築するとき
- 1つのオペレーションの出力を次のオペレーションへの入力として使う複数のGraphQL呼び出しを連鎖させるとき
入力
- 必須: GraphQLエンドポイントURLまたはサービス名(例:
)github - 必須: オペレーションの意図(読み取りまたは書き込みするデータ)
- オプション: 認証トークンまたは方式(デフォルト:GitHubには
CLI認証)gh - オプション: 出力フォーマットの選択(生JSON、jqフィルター済み、変数代入)
手順
ステップ1: スキーマの発見
利用可能な型、フィールド、クエリ、ミューテーションを確認します。
GitHubの場合:
# 利用可能なクエリフィールドの一覧表示 gh api graphql -f query='{ __schema { queryType { fields { name description } } } }' \ | jq '.data.__schema.queryType.fields[] | {name, description}' # 利用可能なミューテーションフィールドの一覧表示 gh api graphql -f query='{ __schema { mutationType { fields { name description } } } }' \ | jq '.data.__schema.mutationType.fields[] | {name, description}' # 特定の型を調べる gh api graphql -f query='{ __type(name: "Repository") { fields { name type { name kind ofType { name } } } } }' | jq '.data.__type.fields[] | {name, type: .type.name // .type.ofType.name}'
汎用エンドポイントの場合:
# curlによる完全なイントロスペクションクエリ curl -s -X POST https://api.example.com/graphql \ -H "Content-Type: application/json" \ -H "Authorization: Bearer $TOKEN" \ -d '{"query":"{ __schema { types { name kind fields { name } } } }"}' \ | jq '.data.__schema.types[] | select(.kind == "OBJECT") | {name, fields: [.fields[].name]}'
期待結果: 利用可能な型、フィールド、またはミューテーションを一覧表示するJSON出力。スキーマレスポンスがエンドポイントに到達可能で認証トークンが有効であることを確認します。
失敗時:
— トークンを確認してください。GitHubの場合は401 Unauthorized
を実行gh auth status
— エンドポイントがイントロスペクションを無効にしている可能性があります。代わりにドキュメントを参照してくださいCannot query field- 接続拒否 — エンドポイントURLとネットワークアクセスを確認してください
ステップ2: オペレーションタイプの特定
タスクにクエリ(読み取り)、ミューテーション(書き込み)、またはサブスクリプション(ストリーム)が必要かどうかを判断します。
| 意図 | オペレーション | 例 |
|---|---|---|
| データ取得 | | リポジトリ詳細の取得、ディスカッションの一覧 |
| 作成/更新/削除 | | ディスカッションの作成、コメントの追加 |
| リアルタイム更新 | | 新しいissueを監視(CLIでは稀) |
GitHub固有のオペレーションについては、GitHub GraphQL API docsを参照してください。
# クイックチェック:ミューテーションは存在するか? gh api graphql -f query='{ __schema { mutationType { fields { name } } } }' \ | jq '.data.__schema.mutationType.fields[].name' | grep -i "discussion"
期待結果: クエリとミューテーションのどちらが必要かが明確になり、正確なオペレーション名(例:
createDiscussion、repository)が特定されます。
失敗時:
- オペレーションが見つからない — より広い用語で検索するか、APIバージョンを確認してください
- クエリとミューテーションの区別が不明 — アクションが状態を変更する場合はミューテーションです
ステップ3: オペレーションの構築
フィールド、引数、変数を使ってGraphQLクエリまたはミューテーションを構築します。
クエリの例 — リポジトリのディスカッションカテゴリを取得:
gh api graphql -f query=' query($owner: String!, $repo: String!) { repository(owner: $owner, name: $repo) { discussionCategories(first: 10) { nodes { id name } } } } ' -f owner="OWNER" -f repo="REPO" | jq '.data.repository.discussionCategories.nodes'
ミューテーションの例 — GitHubディスカッションの作成:
gh api graphql -f query=' mutation($repoId: ID!, $categoryId: ID!, $title: String!, $body: String!) { createDiscussion(input: { repositoryId: $repoId, categoryId: $categoryId, title: $title, body: $body }) { discussion { url number } } } ' -f repoId="$REPO_ID" -f categoryId="$CAT_ID" \ -f title="My Discussion" -f body="Discussion body here"
主要な構築ルール:
- 再利用性のために、インライン値ではなく常に変数(
)を使用する$var: Type! - レスポンスサイズを最小化するために必要なフィールドのみリクエストする
- ページネーション付きコネクションには
とfirst: N
を使用するnodes - すべてのオブジェクト選択に
を追加する — 連鎖に必要になるid
期待結果: 適切な変数、フィールド選択、ページネーションパラメータを持つ構文的に有効なGraphQLオペレーション。
失敗時:
- 構文エラー — ブラケットの対応とトレーリングカンマを確認してください(GraphQLにはトレーリングカンマがない)
- 型の不一致 — スキーマに対して変数の型を確認してください(例:
対ID!
)String! - 必須フィールドの欠落 — スキーマに従って必須入力フィールドを追加してください
ステップ4: CLIによる実行
オペレーションを実行してレスポンスをキャプチャします。
GitHub —
の使用:gh api graphql
# 単純なクエリ gh api graphql -f query='{ viewer { login } }' # 変数を使用 gh api graphql \ -f query='query($owner: String!, $repo: String!) { repository(owner: $owner, name: $repo) { id name } }' \ -f owner="octocat" -f repo="Hello-World" # jqによる後処理 REPO_ID=$(gh api graphql \ -f query='query($owner: String!, $repo: String!) { repository(owner: $owner, name: $repo) { id } }' \ -f owner="OWNER" -f repo="REPO" \ --jq '.data.repository.id')
汎用エンドポイント — curlの使用:
curl -s -X POST "$GRAPHQL_ENDPOINT" \ -H "Content-Type: application/json" \ -H "Authorization: Bearer $TOKEN" \ -d "$(jq -n \ --arg query 'query { users { id name } }' \ '{query: $query}' )"
期待結果: リクエストされたフィールドを含む
dataキーを持つJSONレスポンス。オペレーションが失敗した場合はerrors配列。
失敗時:
- レスポンスに
配列 — メッセージを読んでください。よくある原因:権限の欠落、無効なID、レート制限errors - 空の
— クエリがレコードと一致しなかった。入力値を確認してくださいdata - HTTP 403 — トークンに必要なスコープがない。GitHubの場合は
を確認し、gh auth status
でスコープを追加してくださいgh auth refresh -s scope
ステップ5: レスポンスの解析
JSONレスポンスから必要なデータを抽出します。
# 単一の値を抽出 gh api graphql -f query='{ viewer { login } }' --jq '.data.viewer.login' # リストから抽出 gh api graphql -f query=' query($owner: String!, $repo: String!) { repository(owner: $owner, name: $repo) { issues(first: 5, states: OPEN) { nodes { number title } } } } ' -f owner="OWNER" -f repo="REPO" \ --jq '.data.repository.issues.nodes[] | "\(.number): \(.title)"' # 後で使用するために変数に代入 CATEGORY_ID=$(gh api graphql -f query=' query($owner: String!, $repo: String!) { repository(owner: $owner, name: $repo) { discussionCategories(first: 20) { nodes { id name } } } } ' -f owner="OWNER" -f repo="REPO" \ --jq '.data.repository.discussionCategories.nodes[] | select(.name == "Show and Tell") | .id')
期待結果: 表示またはシェル変数への代入に対応したクリーンで抽出された値。
失敗時:
がnullを返す — フィールドパスが間違っています。まず生のJSONをjq
にパイプして構造を確認してくださいjq .- 1つを期待しているときに複数の値 —
フィルターまたはselect()
を追加してください| first - Unicode問題 — 生の文字列出力のために
フラグをjqに追加してください-r
ステップ6: オペレーションの連鎖
1つのオペレーションの出力を次のオペレーションへの入力として使用します。
# ステップA: リポジトリIDの取得 REPO_ID=$(gh api graphql \ -f query='query($owner: String!, $repo: String!) { repository(owner: $owner, name: $repo) { id } }' \ -f owner="$OWNER" -f repo="$REPO" \ --jq '.data.repository.id') # ステップB: ディスカッションカテゴリIDの取得 CAT_ID=$(gh api graphql \ -f query='query($owner: String!, $repo: String!) { repository(owner: $owner, name: $repo) { discussionCategories(first: 20) { nodes { id name } } } }' \ -f owner="$OWNER" -f repo="$REPO" \ --jq '.data.repository.discussionCategories.nodes[] | select(.name == "Show and Tell") | .id') # ステップC: 両方のIDを使ってディスカッションを作成 RESULT=$(gh api graphql \ -f query='mutation($repoId: ID!, $catId: ID!, $title: String!, $body: String!) { createDiscussion(input: { repositoryId: $repoId, categoryId: $catId, title: $title, body: $body }) { discussion { url number } } }' \ -f repoId="$REPO_ID" -f catId="$CAT_ID" \ -f title="$TITLE" -f body="$BODY" \ --jq '.data.createDiscussion.discussion') echo "Created: $(echo "$RESULT" | jq -r '.url')"
パターン: IDフィールドを前のクエリで常に抽出し、後続のミューテーションに
ID!変数として渡せるようにしてください。
期待結果: 各呼び出しが成功し、IDがオペレーション間で正しく流れる複数ステップのワークフロー。
失敗時:
- 変数が空 — 前のステップがサイレントに失敗した。
を追加して各中間値を確認してくださいset -e - IDフォーマットが間違い — GitHubのノードIDは不透明な文字列(例:
)です。手動で構築しないでくださいR_kgDO... - レート制限 — 呼び出し間に
を追加するか、エイリアスを使ってクエリをバッチ処理してくださいsleep 1
バリデーション
- イントロスペクションクエリがスキーマデータを返す(ステップ1が成功)
- 構築されたクエリが構文的に有効(GraphQLパーサーエラーなし)
- レスポンスに
なしでerrors
キーが含まれるdata - 抽出された値が期待される型と一致する(IDは空でない文字列、カウントは数値)
- 連鎖されたオペレーションがエンドツーエンドで完了する(ミューテーションが前のクエリのIDを使用)
よくある落とし穴
| 落とし穴 | 防止策 |
|---|---|
必須変数型にを付け忘れる | nullability についてスキーマを常に確認してください。ほとんどの入力フィールドはnon-null()です |
| GraphQLでREST IDを使用する | GraphQLは不透明なノードIDを使用します。RESTではなくGraphQL経由で取得してください |
| 大きな結果セットのページネーションを省略する | /とを使用してください |
| クエリせずにIDをハードコードする | IDは環境によって異なります。常に動的にクエリしてください |
配列を無視する | が存在してもエラーを確認してください — 部分的なエラーが起きる可能性があります |
| ネストされたJSONのシェルクォーティング問題 | のフラグを使用するか、に別途パイプしてください |
関連スキル
- scaffold-nextjs-app — GraphQL APIを使用するWebアプリのスキャフォールド
- create-pull-request — GitHubワークフローの自動化(RESTベースの対応物)
- manage-git-branches — API自動化と組み合わせることが多いGitオペレーション
- serialize-data-formats — レスポンス処理で使用するJSONパースパターン