🍓

FastAPIとStrawberryでGraphQL超入門(QueryとMutation)

2023/04/06に公開

はじめに

本記事では、FastAPI+Strawberryで単純なQueryとMutationを実装し呼び出すことを目標にします。

GraphQLとは

GraphQLとはAPI向けのクエリ言語のことです。
主な特徴は以下の通りです。

  • エンドポイントが1つだけ
  • 複数のリソースを取得できる
  • スキーマによる型付けができる
  • Query、Mutation、Subscriptionがある

きちんとした説明はこちらの記事を読むとよさそう。

Strawberryの準備

今回はPythonのFastAPI+Strawberryで実装します。
必要なライブラリをインストールしておきます。

$ pip install fastapi
$ pip install uvicorn
$ pip install strawberry-graphql[fastapi]

公式ドキュメントのコードをコピペしてHello Worldしてみましょう。

main.py
import strawberry

from fastapi import FastAPI
from strawberry.fastapi import GraphQLRouter

@strawberry.type
class Query:
    @strawberry.field
    def hello(self) -> str:
        return "Hello World"

schema = strawberry.Schema(Query)

graphql_app = GraphQLRouter(schema)

app = FastAPI()
app.include_router(graphql_app, prefix="/graphql")

9000番ポートで起動します

$ uvicorn main:app --reload --host 0.0.0.0 --port 9000

localhost:9000/graphqlにアクセスすると下の画像のようなテスターが出てきます。
image
ここで実装したQueryやMutationのレスポンスを確認することができます。
コピペしたコードにはhelloというQueryがあるので、テスター側で

query {
  hello
}

と書いてやることで

{
  "data": {
    "hello": "Hello World"
  }
}

というレスポンスが得られます。
image
これでStrawberryの準備とHello Worldが終わりました🙌

Queryの実装

Queryを実装します。QueryはRESTでいうところのGETにあたります。
Hello World時にすでにQueryは実装されていましたが、Queryは以下のように実装します。

main.py
@strawberry.type
class Query:
    @strawberry.field
    def hello(self) -> str:
        return "Hello World"

@strawberry.typeでデコレートされたQueryクラスのなかで@strawberry.fieldでデコレートされたメソッドを書くことでQueryが実装できます。(メソッド戻り値の型指定を忘れずに!)
では、新しくQueryを追加してみましょう。

main.py
 @strawberry.type
 class Query:
     @strawberry.field
     def hello(self) -> str:
         return "Hello World"
    
+   @strawberry.field
+   def bye(self) -> str:
+       return "Bye!"

これでbyeクエリの実装ができました。
Hello Worldのときと同じようにテスター上で

query {
  bye
}

とすることで

{
  "data": {
    "bye": "Bye!"
  }
}

というレスポンスが得られます。
また、Queryは複数個同時に呼ぶことができます。
例えば

query {
  hello,
  bye
}

上記のクエリは

{
  "data": {
    "hello": "Hello World",
    "bye": "Bye!"
  }
}

というレスポンスを返します。

クエリの戻り値にJSONを指定したいとき
from strawberry.scalars import JSON

strawberryのJSON型が存在しているので、こいつを使えばJSONを戻り値に指定できます。

Mutationの実装

Mutationを実装しましょう。RESTではPOST、PATCH、DELETEにあたるようです。
Mutationは以下のように実装できます。

main.py
+@strawberry.type
+class Mutation:
+   @strawberry.mutation
+   def thousand(self, number:int) -> int:
+       return number * 1000

-schema = strawberry.Schema(Query)
+schema = strawberry.Schema(Query, Mutation)

 graphql_app = GraphQLRouter(schema)

 app = FastAPI()
 app.include_router(graphql_app, prefix="/graphql")

Mutationクラスを追加し、その中で@strawberry.mutationでデコレートされたメソッドを定義しましょう。
もうひとつメソッドを追加してみます。

main.py
 @strawberry.type
 class Mutation:
    @strawberry.mutation
    def thousand(self, number:int) -> int:
        return number * 1000

+   @strawberry.mutation
+   def hundred(self, number:int) -> int:
+       return number * 100

 schema = strawberry.Schema(Query, Mutation)

 graphql_app = GraphQLRouter(schema)

 app = FastAPI()
 app.include_router(graphql_app, prefix="/graphql")

MutationもQueryと同じように1つでも複数でも呼び出すことができます。

# 1つの場合
mutation{
  thousand(number:1)
}

# 複数の場合
mutation{
  thousand(number:1),
  hundred(number:1)
}
//  1つの場合のレスポンス
{
  "data": {
    "thousand": 1000
  }
}

//  複数の場合のレスポンス
{
  "data": {
    "thousand": 1000,
    "hundred": 100
  }
}

APIの叩き方

いままではテスターからQueryやMutationを呼び出していましたが、今度は普通にAPIを叩いてみましょう。
FastAPIを使っているので、http://localhost:9000/docsにアクセスするとSwagger UIにアクセスできます
image
/graphqlエンドポイントはGETPOSTを受け付けていることがわかります。

GETでの叩き方

GETメソッドで呼び出す場合は、クエリパラメータを付けることで呼び出すことができます。

  • Query
    http://localhost:9000/graphql?query=query{hello}にGETでアクセスします。
    レスポンスは
{
    "data": {
        "hello": "Hello World"
    }
}

となり、正常に呼び出せていることがわかります。
image

  • Mutation
    GETメソッドでのMutationの呼び出しは許可されていません。

POSTでの叩き方

http://localhost:9000/graphqlにPOSTでアクセスします。

  • Query
// リクエストボディ
{
  "query":"query{hello}"
}

// レスポンスボディ
{
    "data": {
        "hello": "Hello World"
    }
}

image

  • Mutation
// リクエストボディ
{
  "query":"mutation{thousand(number:1)}"
}

// レスポンスボディ
{
    "data": {
        "thousand": 1000
    }
}

image

これで、GETとPOSTのどちらでもAPIを叩くことができました!

参考

Discussion