GraphQLのGoクライアントを作ってみた
普段GAE/Goでgqlgenを使ってAPIサーバーを開発をしていて、WebクライアントはApolloとGraphQL Code Generatorを使用して開発をしています。 そこで、APIサーバー側のE2Eテストを実現するために、Go言語のGraphQLクライアントが必要になりました。その時の選択肢は以下のような選択肢があります。
この二つのクライアントは非常にシンプルな作りで、GraphQLのAPIを呼び出す分には十分な作りでした。しかし、GraphQLを使ったことある方ならわかるとは思いますが、インプットの型とレスポンスの型が非常に多くなるのと、ネストが深くなります。そうなると、クライアントのQuery毎にレスポンスの型を用意する必要があります。そして、サーバーの開発はgqlgenを使っているので、resolverまでが自動生成される気持ちよさを覚えてしまっているので、GraphQL Code Generatorと同じように定義したQuery毎に型を生成して欲しいという欲望に駆られてしまいました。そして自社のプロダクト開発では、そのクライアント生成を自作して半年ほど使っております。 しかし、今回個人的に、GoでGraphQLのAPIを叩きたい時がきたので、gqlgenのサーバーに大してのみ生成していたものをIntrospection QueryでクライアントのSchema定義から型を生成するようにしたものを公開いたしました。
Annict GraphQL APIでクライアントを生成
今回叩きたかったAPIがAnnict GraphQL APIです。 そして、こちらの使い方を説明されている(Qiitaの記事)https://qiita.com/shimbaco/items/e3f2f8650b08e1e060bdのクライアントを生成してみたいと思います。
まず、gqlgencを入れます。
go get -u github.com/Yamashou/gqlgenc
次に、以下の記事よりAnnictアカウントの設定やアクセストークンを取得してください
export ANNICT_KEY=<アクセストークン>
今回こちらのクライアントは基本的にgqlgenのPlugin機構を使っているので、設定もyaml方式を取っているので、.gqlgenc.yamlを用意します。
model: filename: ./gen/models_gen.go # Schema定義から各オブジェクトを生成するファイルを指定 client: filename: ./gen/client.go # クライアントの実装を生成するファイルを指定 models: # Schema側で指定している型をGoではどの型にするかなどを指定できます Int: model: github.com/99designs/gqlgen/graphql.Int64 Date: model: github.com/99designs/gqlgen/graphql.Time endpoint: url: https://api.annict.com/graphql # APIエンドポイントを指定します headers: # Introspection Queryを投げるのにヘッダーが必要な場合は設定します。 Authorization: "Bearer ${ANNICT_KEY}" # 今回はAnnictのアクセストークンが必要なので、環境変数として、セットしたものをそのままこのように記述して使うことができます query: - "./query/*.graphql" # リクエストで使うqueryの定義ファイルを指定します
それでは/query以下に今回使うqueryを定義していきます。
mkdir query
query/query.grahql
query GetUserName { viewer { username name } } query SearchWorksBySeasons($seasons: [String!], $first: Int!) { searchWorks(seasons: $seasons, first: $first) { edges { node { title watchersCount } } } }
そうしたら、.gqlgenc.yaml
のあるディレクトリで、gqlgencを実行します。すると指定した通り/genに型定義とクライアント、リクエストのインプットとアウトプットの型が生成されます。
それでは、そのクライアントを使ってリクエストを送ってみたいと思います。
main.go
func NewAnnictClient(c *client.Client) *gen.Client { return &gen.Client{Client: c} } func main() { key := os.Getenv("ANNICT_KEY") authHeader := func(req *http.Request) { req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", key)) } ctx := context.Background() annictClient := NewAnnictClient(client.NewClient(http.DefaultClient, "https://api.annict.com/graphql", authHeader)) userName, err := annictClient.GetUserName(ctx) if err != nil { fmt.Fprintf(os.Stderr, err.Error()) os.Exit(1) } fmt.Println(userName.Viewer.Name, userName.Viewer.Username) works, err := annictClient.SearchWorksBySeasons(ctx, []string{"2017-spring"}, 3) if err != nil { fmt.Fprintf(os.Stderr, err.Error()) os.Exit(1) } for _, work := range works.SearchWorks.Edges { fmt.Println(work.Node.Title, work.Node.WatchersCount) } }
gqlgenのpluginとして使う
またこのクライアントの実装はgqlgenのPluginのConfigMutatorを実装しているため, github.com/Yamashou/gqlgenc/clientgenのパッケージをgqlgenのこちらの記事にしたがってインポートすることで、そのサーバーに対応したものを使うことができます。
その他機能について、
GraphQLのスペックについて
GraphQLのQuery, Mutation, Fragmentなど、最低限必要なものは使うことが可能ですが、Subscriptionやdirectiveには追従できていない部分もありますし、何がバグで動かないなどの修正が必要ならissueを投げてもらえると嬉しいです。
Schemaのロード方法について
現在はエンドポイントに大して、Introspection Queryを叩いて、そこからロードしているので、もしSchema.jsonやSchemaファイルからロードする方法を用意してはいません。ので、もし必要ならIssueをお願いします。
最後に
最初にも言いましたが、gqlgenを使用して普段は開発をしています。もっとこのツールを使って開発してくれる人が増えてくれると嬉しいので、ぜひこの記事を読んでいただいた方にはgqlgenを一度使ってみてもらえると嬉しいです。そして、そこでクライアントの悩みが出たら、こちらのgqlgencを使ってみてください。