2020-08-26

GraphQL その2 言語仕様編

今回はより詳細にGraphQLの言語仕様を見ていく
前回同様GitHubのツールを使う

引数(Arguments)

公開されているリポジトリの情報を repository を使って取得してみる
repository の仕様は以下の通り

repository(name: String!owner: String!): Repository

引数として、リポジトリの ownername が必要であることがわかる
僕のリポジトリをターゲットにして、適当な情報を取得してみる

query {
  repository(owner: "hakozaru", name: "study_JavaScript") {
    description
    url
    stargazers {
      totalCount
    }
  }
}

↓レスポンス

{
  "data": {
    "repository": {
      "description": "JSを言語仕様から把握し、ライブラリに振り回されない漢を目指すリポジトリ",
      "url": "https://github.com/hakozaru/study_JavaScript",
      "stargazers": {
        "totalCount": 20
      }
    }
  }
}

ちゃんと取れている〜
stargazers は引数を取るが、必須となる引数はないので省略できていることもわかる↓

stargazers(
  after: String
  before: String
  first: Int
  last: Int
  orderBy: StarOrder
): StargazerConnection!

GraphQLのドキュメント

エイリアス(Aliases)

別名を付けられるアレですが、GraphQLでも使うことができる(ドキュメント)

もし2つのリポジトリの情報を同時に取得したい場合、以下のようなクエリはエラーとなる

query {
  repository(owner: "hakozaru", name: "study_JavaScript") {
    description
  }
  repository(owner: "hakozaru", name: "study_gem") {
    description
  }
}

レスポンスには fieldConflict と返ってきており、重複してしまっていることがわかる

そんな時に使えるのがエイリアスで、以下のように 別名: を先頭につけてあげるだけでよい

query {
  rep1: repository(owner: "hakozaru", name: "study_JavaScript") {
    description
  }
  rep2: repository(owner: "hakozaru", name: "study_gem") {
    description
  }
}

レスポンス

{
  "data": {
    "rep1": {
      "description": "JSを言語仕様から把握し、ライブラリに振り回されない漢を目指すリポジトリ"
    },
    "rep2": {
      "description": "gemとはなんなのか & gemのソース読んでRuby力向上を目指すリポジトリ"
    }
  }
}

フラグメント(Fragments)を使ってクエリをDRYに

↑のエイリアスのクエリは、同じような構造をしている箇所が複数ありちょっとDRYではない

そんな時に使えるのがフラグメントというもので、重複している箇所を共通化することができる

query {
  rep1: repository(owner: "hakozaru", name: "study_JavaScript") {
    ...repDescription
  }
  rep2: repository(owner: "hakozaru", name: "study_gem") {
    ...repDescription
  }
}

fragment repDescription on Repository {
  description
  url
}

レスポンス

{
  "data": {
    "rep1": {
      "description": "JSを言語仕様から把握し、ライブラリに振り回されない漢を目指すリポジトリ",
      "url": "https://github.com/hakozaru/study_JavaScript"
    },
    "rep2": {
      "description": "gemとはなんなのか & gemのソース読んでRuby力向上を目指すリポジトリ",
      "url": "https://github.com/hakozaru/study_gem"
    }
  }
}

あ〜いい感じですわ〜

操作名(Operation Name)

前回の記事で、 query は省略が可能であると書きました
具体的には以下の二つのクエリは等価ということです

query {
  user(login: "hakozaru") {
    bio
  }
}

{
  user(login: "hakozaru") {
    bio
  }
}

下の query が省略されているクエリは「無名操作(anonymous operation)」と呼ばれるもので
実は一回のリクエストで一つしか書くことができないという制約がある

もし二つ無名操作を書いた場合は Operation name is required when multiple operations are present というエラーが表示される

でも俺はトップレベルに二つクエリを書きたいんじゃーという方のために、操作に名前をつけることができる(operation name)
(無名操作を二つ書くことは諦めろ)
query の後に任意の名前をつけるだけ

query userInfo1 { 
  user(login: "hakozaru") {
    bio
  }
}

query userInfo2 { 
  user(login: "hogehoge-san") {
    bio
  }
}

こうすることでクエリを発行する際に、どちらのクエリを発行するのか尋ねられる

1

変数(Variables)

今までの例では hakozaru という文字列をハードコーディングしていたが、これは変数に入れておきたい
GraphQLではちゃんと変数も使うことができる

GitHubのExplorerでは、クエリを書く欄の下にある QUERY VARIABLES から入れることができる
定義した変数は、頭に $ をつけることで参照することができるので、 query に型とともに渡してやればよい

2

(余談だけど、ちょっとクエリを書き換えるだけでvariablesが空にされるんだけど僕だけ?ハイパーストレス)

ミューテーション(Mutations)

前回の記事でルートタイプがどうのと書いたとき、 querymutation があることに触れた
ここまでスルーしていたが、ここでミューテーションについてみていく

公式のドキュメントを見ると、
「サーバーのデータを変更したり、書き込んだりする操作は、明示的にミューテーションを介して送信する必要があるという規則を作っておくとよい」
ということで、何らかの副作用を発生させるクエリは mutation に定義しておけということらしい

GitHub Explorerの mutation のドキュメントを眺めてみても、 create_XXX とか update_XXX みたいな、
いかにも副作用がありそうなクエリが並んでいる

ということで何かクエリを発行してみたいんだけど、リポジトリの作成とかならお手軽かな?
ということで以下のAPIを叩いて、実際にリポジトリが作成されるか確認する
まずは必要な情報の確認

  • mutation を使う
  • createRepository のAPIの仕様は以下の通り
createRepository(input: CreateRepositoryInput!): CreateRepositoryPayload
  Create a new repository.
  • createRepository は引数として inputCreateRepositoryInput とやらを渡す必要がある
    • ドキュメントを見ると、必須パラメータなのは namevisibillity だけ
    • 説明も入れたいので description も渡す
  • createRepository が返すフィールドも確認すると repositorydescription で説明が取得できるようなので、ちゃんと渡した説明が取得できるか確認する

ということで組み立てたクエリが↓

mutation createRepo {
  createRepository(input: {
    name: "new-repo-from-github-graphql-api",
    visibility: PUBLIC,
    description: "GitHubのGraphQL APIを叩いて作成したリポジトリだよ"
  }) {
    repository {
      description
    }
  }
}

叩いてみると

{
  "data": {
    "createRepository": {
      "repository": {
        "description": "GitHubのGraphQL APIを叩いて作成したリポジトリだよ"
      }
    }
  }
}

ちゃんとリポジトリが作成されて description が設定されているっぽい
自分のリポジトリ一覧を見ると

3

ちゃんと作られてるー!

ということで簡単ですがGraphQLの言語仕様でした〜