チラシ裏日記上等!!新館

Webアプリケーションエンジニアの雑記帳。映画とかアニメとかの記事も書きます。

GraphQLのPython実装「Graphene」に入門

個人的にGraphQLの機運が高まってきたので、最近仕事で使っているPythonでの実装であるGrapheneに入門してみました。

GraphQLについて

GraphQLについては多くの入門記事があるのでここでは触れないのですが、id:gfxさん記事が凄くわかりやすいのでお薦めです。

employment.en-japan.com

それを参照して登場人物を覚えたら公式のドキュメントに目を通すとよさそうです。

graphql.github.io

Graphneのサンプル

Grapheneを触っていきます。今回はシンプルに値を取得するクエリのqueryと値を更新するmutationのサンプルを作ってみました。

今回のサンプルコードは次のURLにあります。

github.com

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_フィールド名 で定義するのでタイポすると動かないので注意です。

最後に定義したスキーマである QuerySchema オブジェクトにセットしてクエリを実行できるオブジェクトにします。

実際に値を取得するためのクエリは文字列で定義します。定義したクエリを用いて 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クエリの名前になります。

最後に定義した MyMutationSchema オブジェクトにセットしてクエリを実行できるオブジェクトにします。

mutationのクエリも文字列で定義し、mutationの名前をローワーキャメルケースで書いて更新する対象を定義します。

定義したクエリを用いて schema.execute を実行すると結果が得られます。

Webアプリケーションで使用する場合はmutationメソッド内で実際の値を変更する処理を書くイメージになると思います。

感想

Grapheneに入門してみました。PythonでもGraphQLが出来ることが分かりました。Graphene自体はGraphQLをやる上でのフロントエンドで、内部のビジネスロジックとはそんなに結合しないイメージがあります。

GraphQLに関することだとどのライブラリでも一緒だと思いますが、登場人物が多くてREST APIよりはやることが多いという印象があります。ここでは触れませんでしたがInterfaceやPagenationの概念もあるので使いこなすのは慣れが必要そうです。

APIを叩く側としてはGraphQLは魅力的ですが、サーバサイドの実装はREST APIより難しくなるので、それなりの覚悟が必要になりそうです。