個人的にGraphQLの機運が高まってきたので、最近仕事で使っているPythonでの実装であるGrapheneに入門してみました。
GraphQLについて
GraphQLについては多くの入門記事があるのでここでは触れないのですが、id:gfxさん記事が凄くわかりやすいのでお薦めです。
それを参照して登場人物を覚えたら公式のドキュメントに目を通すとよさそうです。
Graphneのサンプル
Grapheneを触っていきます。今回はシンプルに値を取得するクエリのqueryと値を更新するmutationのサンプルを作ってみました。
今回のサンプルコードは次のURLにあります。
query
queryの凄く簡単な実装を次に示します。
import graphene class Query(graphene.ObjectType): hello = graphene.String(description="hello") def resolve_hello(self, info): """ hello respolver """ return 'world' schema = graphene.Schema(query=Query) query = ''' query SayHello { hello } ''' if __name__ == '__main__': result = schema.execute(query) print(result.data['hello'])
graphene.ObjectType
を継承した Query
でスキーマを定義します。hello
という変数が今回唯一のフィールドになります。フィールドに対応するリゾルバである resolve_hello
はメソッドで定義します。resolve_フィールド名
で定義するのでタイポすると動かないので注意です。
最後に定義したスキーマである Query
をSchema
オブジェクトにセットしてクエリを実行できるオブジェクトにします。
実際に値を取得するためのクエリは文字列で定義します。定義したクエリを用いて schema.execute
を実行すると結果が得られます。
スキーマをObjectType
を継承したクラスで表現するのがおもしろいですね。フィールドとそれに対応するリゾルバを書くと言う流れが型になっているのでスキーマの定義自体はそんなに難しくなさそうです。Webアプリケーションで使用する場合はリゾルバで実際にデータソースから値を取得するコードを書くとよさそうです。
mutation
mutationのサンプルを次に示します。
import graphene class Ship(graphene.ObjectType): name = graphene.String() completion = graphene.String() captain = graphene.String() class ShipInput(graphene.InputObjectType): name = graphene.String(required=True) completion = graphene.String(required=True) captain = graphene.String(required=True) class CreateShip(graphene.Mutation): class Arguments: ship_data = ShipInput(required=True) Output = Ship @staticmethod def mutate(self, info, ship_data=None): ship = Ship( name=ship_data.name, completion=ship_data.completion, captain=ship_data.captain ) return ship class MyMutations(graphene.ObjectType): create_ship = CreateShip.Field() schema = graphene.Schema(mutation=MyMutations) query = ''' mutation myMutations { createShip(shipData:{name:"kongoumk2",completion:"2019/01/31",captain:"nasum"}) { name completion captain __typename } } ''' if __name__ == '__main__': result = schema.execute(query) print(result.data['createShip'])
mutationはちょっと登場人物が多いです。
Mutation
を継承したmutation用のクラス CreateShip
を用意します。その中で引数を定義するためのインナークラス Arguments
を定義し変数に引数として使うためのInputObjectType
を継承した ShipInput
を代入します。ここでの変数名はクエリの引き数名で使用します。
返値のための ObjectType
を継承した Ship
を定義し、 Output
変数に代入します。作成した Ship
は実際のmutation処理を行う mutate
メソッドで返値としても使用します。
作成したmutationをまとめる ObjectType
を継承した MyMutations
を作成し、変数にmutation用のクラス CreateShip
のクラスメソッド Field
の返値をセットします。ここでの変数名はmutationクエリの名前になります。
最後に定義した MyMutation
をSchema
オブジェクトにセットしてクエリを実行できるオブジェクトにします。
mutationのクエリも文字列で定義し、mutationの名前をローワーキャメルケースで書いて更新する対象を定義します。
定義したクエリを用いて schema.execute
を実行すると結果が得られます。
Webアプリケーションで使用する場合はmutationメソッド内で実際の値を変更する処理を書くイメージになると思います。
感想
Grapheneに入門してみました。PythonでもGraphQLが出来ることが分かりました。Graphene自体はGraphQLをやる上でのフロントエンドで、内部のビジネスロジックとはそんなに結合しないイメージがあります。
GraphQLに関することだとどのライブラリでも一緒だと思いますが、登場人物が多くてREST APIよりはやることが多いという印象があります。ここでは触れませんでしたがInterfaceやPagenationの概念もあるので使いこなすのは慣れが必要そうです。
APIを叩く側としてはGraphQLは魅力的ですが、サーバサイドの実装はREST APIより難しくなるので、それなりの覚悟が必要になりそうです。