クエリーに合わせた構造体を書くだけでGithub GraphQL API を扱えるGoのライブラリ[githubql]
golangとGraphQLの勉強中。
今回はGoでGithub APIのGraphQLのクライアント githubql を使ってみました。
githubqlの使い方
基本的にはGraphQLのqueryやmutationに合わせたGoの構造体を定義するだけです。
まずgithubqlのページに載っているサンプルを見てみましょう。 以下のようなシンプルなqueryの場合、
query { viewer { login createdAt } }
このように構造体を定義し、
var query struct { Viewer struct { Login githubql.String CreatedAt githubql.DateTime } }
client.Query
を実行すると戻り値にGithubのGraphQL APIの結果が入っています。
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で書くと以下のようになります。
src := oauth2.StaticTokenSource(
&oauth2.Token{AccessToken: os.Getenv("GITHUB_TOKEN")},
)
httpClient := oauth2.NewClient(context.Background(), src)
client := githubql.NewClient(httpClient)
続いてRepositoryとLanguageの構造体を定義します。
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の中に入れ子で書けますが、今回は分けて書いてます。
var query struct { Search struct { Nodes []struct { Repository `graphql:"... on Repository"` } } `graphql:"search(first: 5, query: $q, type: $searchType)"` }
Searchに渡すクエリパラメータを利用する時は、mapを作って渡します。
variables := map[string]interface{}{ "q": githubql.String("GraphQL"), "searchType": githubql.SearchTypeRepository, } err := client.Query(context.Background(), &query, variables)
以下が全体のソースです。
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
以上です。