#author("2019-11-13T08:07:39+00:00","","")
#author("2019-11-13T11:33:53+00:00","","")
#mynavi();
#setlinebreak(on);

* 概要 [#h3339617]
#html(<div style="padding-left: 10px;">)

GraphQLはFacebookにより開発されたオープンソースの言語。
- Rest のようにリソース毎に複数のエンドポイントは持たない。(エンドポイントは1つ)
- 事前定義されたスキーマとリクエスト時のクエリの内容に応じて処理が行われる
などの特徴がある。

以降では、GraphQLの考え方と簡単な環境構築を行う。
以降では、GraphQLサーバ環境の構築と簡単な動作確認を行う。
#html(</div>)

* 目次 [#zefea870]
#contents
- 参考
-- https://graphql.org/learn/
-- https://docs.graphene-python.org/en/latest/quickstart/
-- https://github.com/graphql-python/graphene/blob/master/examples/simple_example.py
- 関連
-- XXXXXXX

* GraphQLの基礎 [#ia8c2b77]
#html(<div style="padding-left: 10px;">)

** クエリとミューテーション [#ybc8a2b8]
#html(<div style="padding-left: 10px;">)

#TODO

*** Fields [#pfa3b570]
#html(<div style="padding-left: 10px;">)
https://graphql.org/learn/queries/#fields
#html(</div>)

*** Arguments [#sff1c3fc]
#html(<div style="padding-left: 10px;">)
https://graphql.org/learn/queries/#arguments
#html(</div>)

*** Aliases [#cff67c85]
#html(<div style="padding-left: 10px;">)
https://graphql.org/learn/queries/#aliases
#html(</div>)

*** Fragments [#e76c2dce]
#html(<div style="padding-left: 10px;">)
https://graphql.org/learn/queries/#fragments
#html(</div>)

*** Operation Name [#b72b325a]
#html(<div style="padding-left: 10px;">)
https://graphql.org/learn/queries/#operation-name
#html(</div>)

*** Variables [#m597b6b7]
#html(<div style="padding-left: 10px;">)
https://graphql.org/learn/queries/#variables
#html(</div>)

*** Directives [#r00f8674]
#html(<div style="padding-left: 10px;">)
https://graphql.org/learn/queries/#directives
#html(</div>)

*** Mutations [#a59ea6e6]
#html(<div style="padding-left: 10px;">)
https://graphql.org/learn/queries/#mutations
#html(</div>)

*** Inline Fragments [#y9b73e9f]
#html(<div style="padding-left: 10px;">)
https://graphql.org/learn/queries/#inline-fragments
#html(</div>)

#html(</div>)
// クエリとミューテーション

** スキーマとタイプ [#c745f6f1]
#html(<div style="padding-left: 10px;">)

#TODO
*** Type System [#ze68b00b]
#html(<div style="padding-left: 10px;">)
#html(</div>)

*** Type Language [#x3b3e0e0]
#html(<div style="padding-left: 10px;">)
#html(</div>)

*** Object Types and Fields [#d5dacea4]
#html(<div style="padding-left: 10px;">)
#html(</div>)

*** Arguments [#c5372518]
#html(<div style="padding-left: 10px;">)
#html(</div>)

*** The Query and Mutation Types [#l8968084]
#html(<div style="padding-left: 10px;">)
#html(</div>)

*** Scalar Types [#db6de978]
#html(<div style="padding-left: 10px;">)
#html(</div>)

*** Enumeration Types [#ze5273d0]
#html(<div style="padding-left: 10px;">)
#html(</div>)

*** Lists and Non-Null [#h9b08390]
#html(<div style="padding-left: 10px;">)
#html(</div>)

*** Interfaces [#ze6d58f3]
#html(<div style="padding-left: 10px;">)
#html(</div>)

*** Union Types [#wbc638e2]
#html(<div style="padding-left: 10px;">)
#html(</div>)

*** Input Types [#ycab3418]
#html(<div style="padding-left: 10px;">)
#html(</div>)


#html(</div>)


#html(</div>)

* サーバ側の環境構築 [#m4f52d38]
#html(<div style="padding-left: 10px;">)

pythonライブラリ graphene を使用してサーバ環境を構築する。

** 準備 [#j4dcd023]
#html(<div style="padding-left: 10px;">)

pythonの仮想環境を作って graphene をインストールしておく。
※WebAPIとして提供する為、flask と flask-graphql も同時にインストール。
#myterm2(){{
pip install pipenv
pipenv --python 3.7
pipenv install graphene
pipenv install flask
pipenv install flask-grapphql
pipenv install flask-graphql
}}

動作確認用に仮想環境のシェルを起動しておく
#myterm2(){{
pipenv shell
}}

#html(</div>)

** スキーマ 及び リゾルバ関数の定義 [#e2398928]
#html(<div style="padding-left: 10px;">)

schema.py
#mycode2(){{
import graphene
# サンプルデータ
sample_users = { 
    "1": {"id":1, "name":"Ichiro", "age":10},
    "2": {"id":2, "name":"Jiro", "age":20},
    "3": {"id":3, "name":"Saburo", "age":30}
}

class User(graphene.ObjectType):  # graphene.ObjectType を継承してスキーマ定義
    """スキーマの定義."""

class User(graphene.ObjectType):
    id = graphene.ID()
    name = graphene.String()
    age = graphene.Int()


class Query(graphene.ObjectType):
    """リゾルバ関数の定義."""
    user = graphene.Field(User)

    def resolve_user(self, info, id):  #「resolve_変数名」でリゾルバ関数を定義する
        return User(id=1, name="Taro", age=20)
    user = graphene.Field(User, id=graphene.ID())
    users = graphene.Field(graphene.List(User))

# スキーマを生成
schema = graphene.Schema(query=Query)
    def resolve_user(self, info, id): # resolve_変数名
        """ID指定のユーザ取得."""
        user_info = None
        if id:  # if info.variable_values["id"] でも可
            user_info = sample_users.get(id)  # sample_users.get(info.variable_values["id"])
        if user_info:
            return User(**user_info)
        else:
            return None

    def resolve_users(self, info, **kwargs):
        """ユーザ一覧の取得."""
        return [ User(**sample_users[id]) for id in sample_users]


class CreateUser(graphene.Mutation):
    class Arguments:
        name = graphene.String()
        age = graphene.Int()

    created = graphene.Boolean()
    user = graphene.Field(lambda: User)

    def mutate(root, info, name, age):
        id = len(sample_users) + 1 
        sample_users[str(id)] = {"id":id, "name":name, "age":age}
        user = User(id=id, name=name, age=age)
        return CreateUser(user=user, created=True)


class MyMutations(graphene.ObjectType):
    create_user = CreateUser.Field()


schema = graphene.Schema(query=Query, mutation=MyMutations)
}}

#html(</div>)

** スキーマ単体での動作確認 [#b2280a8e]
#html(<div style="padding-left: 10px;">)

test_schema.py
#mycode2(){{
from sample_schema import schema
from schema import schema

if __name__ == "__main__":

    # ユーザを作成
    query = """ 
        query something {
          user(id: "1") {
            id
            name
        mutation createMutation {
            createUser(name:"Shiro", age:40) {
                user {
                    id
                    name
                    age
                }
                created
            }
        }
    """
    result = schema.execute(query)
    print(result.data)

    # 一覧を取得
    query = """ 
        query ListQuery {
          users {
            id,
            name,
            age
          }
        }
    """
    result = schema.execute(query)
    #result = schema.execute(query, {"id": "1"})
    if result.errors:
        print(result.errors)
    else:
        print(result.data)
    print(result.data)

    # IDを指定して取得
    query = """ 
        query SampleQuery($id: ID) {
          user(id: $id) {
            id,
            name,
            age
          }
        }
    """
    result = schema.execute(query, variable_values={"id": 3})
    print(result.data)
}}

動作確認の実行
#myterm2(){{
python test_schema.py
OrderedDict([('user', OrderedDict([('id', '1'), ('name', 'Taro'), ('age', 20)]))])
OrderedDict([('createUser', OrderedDict([('user', OrderedDict([('id', '4'), ('name', 'Shiro'), ('age', 40)])), ('created', True)]))])
OrderedDict([('users', [OrderedDict([('id', '1'), ('name', 'Ichiro'), ('age', 10)]), OrderedDict([('id', '3'), ('name', 'Saburo'), ('age', 30)]), OrderedDict([('id', '2'), ('name', 'Jiro'), ('age', 20)]), OrderedDict([('id', '4'), ('name', 'Shiro'), ('age', 40)])])])
OrderedDict([('user', OrderedDict([('id', '3'), ('name', 'Saburo'), ('age', 30)]))])
}}

#html(</div>)

** Webサーバの実装 [#n51fc439]
#html(<div style="padding-left: 10px;">)

app.py
#mycode2(){{
from schema import schema
from flask import Flask
from flask_graphql import GraphQLView

app = Flask(__name__)
app.debug = True

app.add_url_rule(
    '/',
    view_func=GraphQLView.as_view(
        'graphql',
        schema=schema,
        graphiql=True  # テスト時などにブラウザから利用できるAPIコンソールをONにしておく
    )   
)

if __name__ == '__main__':
    app.run()
}}

サーバ起動
#myterm2(){{
python app.py
}}

#html(</div>)

** WebAPIとしての動作確認 [#td188c56]
#html(<div style="padding-left: 10px;">)

#myterm2(){{
curl http://127.0.0.1:5000/ --data query='{ user(id: "1") { id, name, age&#125;&#125;'
# 一意検索
curl http://127.0.0.1:5000/ --data query='query { user(id: "1") { id, name, age&#125;&#125;'
{"data":{"user":{"id":"1","name":"Taro","age":20&#125;&#125;&#125;

# 一覧検索
curl http://127.0.0.1:5000/ --data query='query { users{ id, name&#125;&#125;'
{"data":{"users":[{"id":"3","name":"Saburo"},{"id":"1","name":"Ichiro"},{"id":"2","name":"Jiro"}]&#125;&#125;

# 追加
curl http://127.0.0.1:5000/ --data query='mutation { createUser(name:"Shiro", age:40) { user {id, name, age}, created &#125;&#125;'
{"data":{"createUser":{"user":{"id":"4","name":"Shiro","age":40},"created":true&#125;&#125;&#125;
}}
※ graphiql=True で起動している場合は、ブラウザから任意のQueryを発行する為の、コンソール画面も提供される。

尚、graphiql=True で起動している場合は、ブラウザから任意のQueryを発行する為のコンソール画面も提供されるので、そちらから確認する事も可能。

#html(</div>)


#html(</div>)


トップ   一覧 単語検索 最終更新   ヘルプ   最終更新のRSS