1. はじめに
「Azure FunctionsとGrapheneでCosmosDBデータ操作を行う」ではAzure FunctionsでGraphQL APIのQueryオペレーションを実装していましたが、Mutationオペレーションを実装していなかったので、今回はMutationの実装を進めていきたいと思います。
前回までの事前準備や実施内容はこの記事とこの記事を参照ください。
2. GraphQLクエリのミューテーションの定義
「Azure FunctionsとGrapheneでCosmosDBデータ操作を行う」で実施してきたことに引き続き、Grapheneを利用してGraphQLのMutationを実装していきます。実装についてはGraphene公式ドキュメントを参照していきます。
ミューテーションはデータソースに対する書き込みとその実行結果の取得を行う、GraphQLオペレーションになります。(クエリはデータソースに対する読み取り結果の取得のみ)
スキーマを定義し、次にリゾルバーを定義し、最後にリゾルバー内部にデータソースに対する処理を実装するという流れは前回同様ですが、リゾルバーの指定がQuery型で定義してきたものと若干異なります。
2.1. Mutationの各操作に対応するリゾルバー定義
GraphQLのMutation型の各操作(フィールド)とリゾルバー関数を対応づけるにあたり、操作毎にMutationを継承したクラスを用意しておきます。
# Mutation型の各操作と対応づけるリゾルバーの定義
class CreateItem(graphene.Mutation):
class Arguments:
item = ItemInput(required=True) #ミューテーションのAugument
Output = Item #ミューテーションの実行結果の型
def mutate(self, info, item): #Mutation実行時に呼び出される関数
i = Itemオブジェクトを作成するDB操作
return i
上記の例ではInput型のオブジェクトを利用しています。こちらはInputObjectTypeを継承したクラスを用意し、QueryやMutationの各操作にArgumentsとして渡せるようにします。
# idとmessageというフィールドを持つinput型の定義
class ItemInput(graphene.InputObjectType): #inputの定義
id = graphene.String(required=True)
message = graphene.String()
2.2. Mutation型の定義
GraphQLのMutation型もQuery型と同様にObjectTypeを継承したクラスとして用意します。
Mutation型ではフィールド名とMutationを継承したクラスを対応づけておくと、GraphQLクエリのミューテーションに記述されたフィールド名に対応するリゾルバーが呼び出されるという流れになります。(Mutation型とMutationを継承したクラスで紛らわしい...)
# createItemというフィールドを持つMutation型の定義
class Mutation(graphene.ObjectType):
create_Item = CreateItem.Field()
なおsnake_caseはcamelCaseに置き換えてくれるとのこと。
2.3. ルートクエリ宣言とミューテーションの実行
GraphQLのクエリを実行するにあたり、SchemaクラスでmutationとMutation型を対応づけ(ルートクエリ型の宣言)しておくことで、実際にミューテーションを実行(executeメソッドの呼び出し)する際に対応するMutationのリゾルバーの関数(というかクラスか)が呼び出されて処理が実行されるようになります。これはQuery型の場合と同様の動きになります。
schema = graphene.Schema(
query=Query,
mutation=Mutation #ここを追加
)
schema.execute("GraphQLのミューテーション")
2.4. GraphQL関連のコード
今回新たに追加したGraphQL関連のコードは以下になります。
#全体のコードはGitHubにはPUSHしていますが整理中です。
# input DBItemInput
class DBItemInput(graphene.InputObjectType):
id = graphene.String(required=True)
owner = graphene.String(required=True)
partitionKey = graphene.ID(required=True)
message = graphene.String()
addition = graphene.String()
class CreateItem(graphene.Mutation):
class Arguments:
item = DBItemInput(required=True)
Output = DbItem
def mutate(self, info, item):
results = DatabaseConnection().create_item(item).pop()
i = DbItem.__new__(DbItem)
i.__dict__.update(results)
return i
class DeleteItem(graphene.Mutation):
class Arguments:
item = DBItemInput(required=True)
Output = DbItem
def mutate(self, info, item):
results = DatabaseConnection().delete_item(item)
if results.__len__() > 0:
i = DbItem.__new__(DbItem)
i.__dict__.update(results[0])
return i
return None
class UpsertItem(graphene.Mutation):
class Arguments:
item = DBItemInput(required=True)
Output = DbItem
def mutate(self, info, item):
results = DatabaseConnection().upsert_item(item)
i = DbItem.__new__(DbItem)
i.__dict__.update(results)
return i
# type Mutation
class Mutation(graphene.ObjectType):
create_Item = CreateItem.Field()
delete_Item = DeleteItem.Field()
upsert_Item = UpsertItem.Field()
class GraphQL:
def __init__(self):
self.schema = graphene.Schema(
query=Query,
mutation=Mutation
)
pass
def query(self, query):
results = self.schema.execute(query)
return json.dumps(results.data)
上記のスキーマをGraphQLスキーマ定義言語で記述すると以下のような感じになります。
input DBItemInput {
id: String!
owner: String!
partitionKey: String!
message: String
addition: String
}
type Mutation {
createItem(DBItemInput!): DbItem
deleteItem(DBItemInput!): DbItem
upsertItem(DBItemInput!): DbItem
}
schema {
query: Query
mutation: Mutation
}
4. GraphQLミューテーションの実行確認
実装した関数にGraphQLのミューテーションを発行し、レスポンスを見たいと思います。実行方法については、
クエリの発行はcurlコマンドを使うほか、GraphiQLやInsomniaを使う手があると思います。
curlコマンドの場合には以下でクエリを発行できます。
curl -X POST -H "Content-Type: application/json" -d '{"query": "mutation { createItem (item: {id: "id1", owner: "testuser", partitionKey: 1, message: "test message1", addition: "addtion message 1"}) { id message } }"}' http://localhost:7071/api/MyFunction
今回発行したGraphQLミューテーションは以下になります。Insomniaの場合はURLを指定し以下ミューテーションを発行するとレスポンスが得られます。
mutation {
createItem(item: {id: "id1", owner: "testuser", partitionKey: 1, message: "test message1", addition: "addtion message 1"}) {
id
message
}
}
GrahpQLの利用や実装を進めるにあたり、GraphQLの仕様や用語に関してもっと理解をしておきたいですね。