Github GraphQLのクエリーに対応する構造体を作るだけでレスポンスのJSONとマッピングしてくれるGoのライブラリ「githubql」

GoでGithub APIのGraphQLのクライアント [githubql](https://github.com/shurcooL/githubql) を使ってみました。

# githubqlの使い方
基本的にはGraphQLのqueryやmutationに合わせたGoの構造体を定義するだけです。

まずgithubqlのページに載っているサンプルを見てみましょう。
以下のようなシンプルなqueryの場合、

```
query {
	viewer {
		login
		createdAt
	}
}
```

このように構造体を定義し、

```go
var query struct {
	Viewer struct {
		Login     githubql.String
		CreatedAt githubql.DateTime
	}
}
```

`client.Query`を実行すると戻り値にGithubのGraphQL APIの結果が入っています。

```go
err := client.Query(context.Background(), &query, nil)
if err != nil {
	// Handle error.
}
fmt.Println("    Login:", query.Viewer.Login)
fmt.Println("CreatedAt:", query.Viewer.CreatedAt)
```


# 今回試すクエリー（GraphQL Explorerの場合）
GithubのGraphQL APIはこちらで簡単に実行結果を確認することができます。
https://developer.github.com/v4/explorer/

今回はGithubにあるGraphQL関連のRepositoryとその言語を5件抽出するクエリーを書きました。

```
query {
    search(query: "GraphQL", first: 5, type: REPOSITORY) {
      edges {
        node {
          ... on Repository {
            nameWithOwner,
            url,
            languages(first:5) {
              edges {
                node {
                  ... on Language {
                    name,
                    color
                  }
                }
              }
            }
          }
        }
      }
    }
  }
```


### 結果

```
{
  "data": {
    "search": {
      "edges": [
        {
          "node": {
            "nameWithOwner": "facebook/graphql",
            "url": "https://github.com/facebook/graphql",
            "languages": {
              "edges": [
                {
                  "node": {
                    "name": "Shell",
                    "color": "#89e051"
                  }
                }
              ]
            }
          }
        },
        {
          "node": {
            "nameWithOwner": "graphql-go/graphql",
            "url": "https://github.com/graphql-go/graphql",
            "languages": {
              "edges": [
                {
                  "node": {
                    "name": "Go",
                    "color": "#375eab"
                  }
                }
              ]
            }
          }
        },

・・・以下省略

```

# githubqlで書く場合
上記のクエリーをgithubqlを使ってGoで書くと以下のようになります。

Githubのトークンを使ってclientを初期化します。

```go
src := oauth2.StaticTokenSource(
	&oauth2.Token{AccessToken: os.Getenv("GITHUB_TOKEN")},
)
httpClient := oauth2.NewClient(context.Background(), src)

client := githubql.NewClient(httpClient)
```


続いてRepositoryとLanguageの構造体を定義します。

```go
type Language struct {
	Name  githubql.String
	Color githubql.String
}

type Repository struct {
	NameWithOwner   githubql.String
	Url     githubql.String
	Languages struct {
		Nodes []struct {
			Language `graphql:"... on Language"`
		}
	} `graphql:"languages(first: 5)"`
}
```
GraphQLの引数を指定したいときは構造体に`graphql:"... on Language"`のようにタグをつけます。


Searchも構造体で定義します。最初はここが分からなくて少しハマりました。先程のRepositoryの定義もSearchの中に入れ子で書けますが、今回は分けて書いてます。

```go
var query struct {
	Search struct {
		Nodes []struct {
			Repository `graphql:"... on Repository"`
		}
	} `graphql:"search(first: 5, query: $q, type: $searchType)"`
}
```


Searchに渡すクエリパラメータを利用する時は、mapを作って渡します。

```go
variables := map[string]interface{}{
	"q": githubql.String("GraphQL"),
	"searchType":  githubql.SearchTypeRepository,
}

err := client.Query(context.Background(), &query, variables)
```


以下が全体のソースです。

```go:graphql-client.go
package main

import (
	"golang.org/x/oauth2"
	"os"
	"fmt"
	"context"
	"github.com/shurcooL/githubql"
)

func main() {
	src := oauth2.StaticTokenSource(
		&oauth2.Token{AccessToken: os.Getenv("GITHUB_TOKEN")},
	)
	httpClient := oauth2.NewClient(context.Background(), src)

	client := githubql.NewClient(httpClient)

	type Language struct {
		Name  githubql.String
		Color githubql.String
	}

	type Repository struct {
		NameWithOwner   githubql.String
		Url     githubql.String
		Languages struct {
			Nodes []struct {
				Language `graphql:"... on Language"`
			}
		} `graphql:"languages(first: 5)"`
	}

	var query struct {
		Search struct {
			Nodes []struct {
				Repository `graphql:"... on Repository"`
			}
		} `graphql:"search(first: 5, query: $q, type: $searchType)"`
	}

	variables := map[string]interface{}{
		"q": githubql.String("GraphQL"),
		"searchType":  githubql.SearchTypeRepository,
	}

	err := client.Query(context.Background(), &query, variables)
	if err != nil {
		fmt.Println(err)
	}

	for _, repo := range query.Search.Nodes {
		fmt.Println("---------")
		fmt.Println(repo.NameWithOwner)
		fmt.Println(repo.Url)
		for _, lang := range repo.Languages.Nodes {
			fmt.Println(lang.Name)
			fmt.Println(lang.Color)
		}
	}
}
```


### 実行結果
クエリーで定義した構造体に、GraphQL APIからのレスポンスのJSONがマッピングされています。

```
---------
facebook/graphql
https://github.com/facebook/graphql
Shell
#89e051
---------
graphql-go/graphql
https://github.com/graphql-go/graphql
Go
#375eab
---------
Youshido/GraphQL
https://github.com/Youshido/GraphQL
PHP
#4F5D95
---------
graphql-elixir/graphql
https://github.com/graphql-elixir/graphql
Elixir
#6e4a7e
Erlang
#B83998
---------
GraphQLSwift/GraphQL
https://github.com/GraphQLSwift/GraphQL
Swift
#ffac45
```

以上です。

## 参考
- https://github.com/shurcooL/githubql
- https://github.com/shurcooL/graphql