Go言語でgraphqlを触ってみた
ゆるっとアドベントカレンダー Advent Calendar 2018 2日目です。
最近ハッカソンでGraphqlを使って開発を行っているので、今回はGraphqlの復習で簡単なGraphqlサーバをGo言語を使って実装してみます。
Graphqlとは?
GraphQL is a query language for APIs and a runtime for fulfilling those queries with your existing data
GraphqlはAPIへ問い合わせるためのクエリ言語になります。一つのエンドポイントを用意して、予め定義しておいたGraphqlのクエリを投げることで必要な情報を持ってくることができます。
今回使う外部ライブラリ
- labstack/echo
- graphql-go/graphql
- graphql-go/handler
今回実装したもの
localhost:8080/graphqlにブラウザでアクセスするとGraphqlのIDEが起動して、Graphqlのqueryを投げると下の写真の右のようにdataが返ってくるようなものになります。
今回実装したものは↓のGithubに置いておきます。
解説
基本的にgraphql-go/graphqlのexampleを参考に実装しています。
まずREST APIのように何か投げたら何か返ってくるものをGraphqlで実装したいためSchemaを定義します。ここで気をつけることは投げられるqueryにはQueryとMutationがあります。SchemaConfigを見ているとSubscriptionがあるのですが調べたかんじだとリアルタイムのデータを取得するときに使うらしいです。まだ使ったことはないので今回は触れません。
QueryとMutationなのですが、QueryはCRUDでいうとCの部分で、単にデータを取得したいときに使います。MutationはR、U、Dの部分で、データに変更を加えたい際に使います。
今回はSampleとしてQueryとMutationを使ってschemaを定義します。
仕様は以下のとおりです。
- Query
- IDとPasswordを渡したらMessageが返ってくる
- Mutation
- UsernameとAgeを渡したらMessageが返ってくる(Mutationなので何かしらの更新処理を入れる必要があるが今回は省略します)
QueryのSchemaの実装
graphql-sample/query.go at master · jon20/graphql-sample · GitHub
func SetQuery() graphql.Fields { query := graphql.Fields{ "SampleQuery": &graphql.Field{ Type: SampleQueryType(), Args: SampleQueryArgs(), Resolve: SampleQueryResolve, }, } return query } func SampleQueryType() *graphql.Object { sampleType := graphql.NewObject(graphql.ObjectConfig{ Name: "SampleQuery", Fields: graphql.Fields{ "Message": &graphql.Field{ Type: graphql.String, }, }, }) return sampleType } func SampleQueryArgs() map[string]*graphql.ArgumentConfig { sampleArgs := graphql.FieldConfigArgument{ "ID": &graphql.ArgumentConfig{ Type: graphql.NewNonNull(graphql.String), }, "Password": &graphql.ArgumentConfig{ Type: graphql.NewNonNull(graphql.String), }, } return sampleArgs } func SampleQueryResolve(params graphql.ResolveParams) (interface{}, error) { resp := &models.SampleMutateResp{ Message: "Hello, This is SampleQuery", } return resp, nil }
汚い実装ですが、肝となるのはSetQuery()です。Fieldsに定義したいFieldを定義します。FieldはArgsがqueryを使うときに渡す値の定義、Resolveにはqueryを叩いたときの処理を渡してあげます。
MutationのSchemaの実装
graphql-sample/mutation.go at master · jon20/graphql-sample · GitHub
func SetMutation() graphql.Fields { mutateQuery := graphql.Fields{ "SampleMutate": &graphql.Field{ Type: SampleMutateType(), Args: SampleMutateArgs(), Resolve: SampleMutateResolve, }, } return mutateQuery } func SampleMutateType() *graphql.Object { sampleType := graphql.NewObject(graphql.ObjectConfig{ Name: "SampleMutate", Fields: graphql.Fields{ "Message": &graphql.Field{ Type: graphql.String, }, }, }) return sampleType } func SampleMutateArgs() map[string]*graphql.ArgumentConfig { sampleArgs := graphql.FieldConfigArgument{ "Username": &graphql.ArgumentConfig{ Type: graphql.NewNonNull(graphql.String), }, "Age": &graphql.ArgumentConfig{ Type: graphql.NewNonNull(graphql.Int), }, } return sampleArgs } func SampleMutateResolve(params graphql.ResolveParams) (interface{}, error) { resp := &models.SampleMutateResp{ Message: "Hello, This is SampleMutate", } return resp, nil }
実装手順としてはqueryと同じです。どちらも共通して気をつけるポイントとしては指定できる型です。Scalar型があるのですが公式のDocumentを見たかんじだとID、String、Int、Float、Booleanだけになります。もしそれ以外の型を使いたい場合は自分で定義しなければなりません。
SchemaのSetupとIDEの用意
graphql-sample/graphql.go at master · jon20/graphql-sample · GitHub
func GraphqlSetting() http.Handler { query := fields.SetQuery() mutateQuery := fields.SetMutation() rootMutation := graphql.NewObject(graphql.ObjectConfig{Name: "RootMutation", Fields: mutateQuery}) rootQuery := graphql.NewObject(graphql.ObjectConfig{Name: "RootQuery", Fields: query}) schema, err := graphql.NewSchema(graphql.SchemaConfig{ Query: rootQuery, Mutation: rootMutation, }) if err != nil { panic(err) } h := handler.New(&handler.Config{ Schema: &schema, Pretty: true, GraphiQL: false, Playground: true, }) return h }
GraphqlのIDEはhandler.Config{}のところで設定できます。指定できるIDEの種類としてはgraphiqlとgraphql-playgroundがあります。今回はgraphql-playgroundを使います。
Graphqlサーバの立ち上げ
graphql-sample/main.go at master · jon20/graphql-sample · GitHub
func main() { route := echo.New() route.POST("/graphql", echo.WrapHandler(graphql.GraphqlSetting())) route.GET("/graphql", echo.WrapHandler(graphql.GraphqlSetting())) route.Logger.Fatal(route.Start("localhost:8080")) }
あとは localhost:8080/graphqlのroutingを追加するためechoを使います。
完成!!
定義したSchemaは↓のように見れます。
Queryに定義したqueryの実行結果 Mutationに定義したqueryの実行結果
まとめ
- 単一エンドポイントにまとめられるのでREST APIで実装する場合と比べてエンドポイントの管理が楽になりそう
- Documentを自動で生成してくれるのでSwaggerなどを用いてDocumentを実装する手間が省ける。
- ファイルアップロードとかを実装する場合はどうじっそうすればいいのだろうか?