Django × Strawberry GraphQL 入門 - 基本設定とスキーマ設計
はじめに#
DjangoでGraphQL APIを開発したい初心者向けに、Strawberry を用いた基本的な設定とスキーマ設計を解説します
本家のサイトでは断片的に書かれていますが、体系的にかかれていないため、ここでは導入方法、具体的なファイル構成について説明します。
導入後の完成形の image も github に上げておきます。
1. strawberryのインストール#
djangoはインストールされている前提(startproject ですでにプロジェクトは存在済み)です。つまり django の helloworld は終わっている状態を仮定しています。
必要なライブラリはstrawberry-graphql
のみですので、これをインストールします。
pip install strawberry-graphql
私はdockerで開発し、ローカルではryeで作業をしていますが、人によっては virtualenv, poetry, pip 等々あるとおもいますので適時入れ替えてください
2. api アプリの作成#
djangoであたらしいapiアプリを作ります。もし既存のアプリにインストールするのであれば別にstartappしなくても構いません。
python manage.py startapp api # manage.py があるディレクトリで実行
settings.py に api を追加します。
``INSTALLED_APPS = [
...
"api"
]
補足: migrations#
人によっては,api の models.py を作る人がいるかもしれません。 モデル編集後は作成後は migratinos & migrate をしておきます。
$ python manage.py makemigrations api
$ python manage.py migrate
3. プロジェクトのurls.pyに追加#
プロジェクトの urls.py を編集して、次の項目を追加します。
path("api/", include("api.urls")),
4. migration, query 用のファイルを作成#
mutations.py と queries.py というファイルを作成します。graphqlのmutationとqueryは各々に書きます。
今、apiフォルダにいるとします。フォルダを3つ作り、先の2つのファイルをタッチします。
mkdir graphql
mkdir graphql/mutations
mkdir graphql/queries
touch graphql/__init__.py # __init__.py は、Python にディレクトリをパッケージとして認識させるためのファイル
touch graphql/mutations/mutations.py
touch graphql/queries/queries.py
こういう構成になっているはずです。
├── graphql
│ ├── queries
│ │ └── queries.py
│ ├── mutations
│ │ └── mutations.py
│ └── __init__.py
説明用にこういうフォルダ構成やファイル名にしましたが、次に作るschema.pyと整合性があれば問題ありません。
5. schema.pyの作成#
api フォルダの中に schema.py を作成します。中身は次のとおりです。
import strawberry
from .graphql.mutations.mutations import Mutation # フォルダ構成はまかせるが、上の説明と矛盾がないように
from .graphql.queries.queries import Query
schema = strawberry.Schema(query=Query, mutation=Mutation)
6. アプリにurls.pyを作成#
続いて apiフォルダ内で urls.py を作ります。
内容は次のとおりです。
from strawberry.django.views import GraphQLView
from django.urls import path
from django.views.decorators.csrf import csrf_exempt
from .schema import schema
urlpatterns = [
path(
"graphql/",
csrf_exempt(GraphQLView.as_view(schema=schema, graphql_ide="apollo-sandbox")),
),
]
graphql-ide
という引数にapollo-sandbox
を指定します。graphql-ide
は指定しなくてもいいですがapolloはおすすめです。
csrf_exmpt
メソッドでくくっていますが、これはなくても動きます。これは、CSRF (Cross-Site Request Forgery) 対策を無効化します。API の場合は必要に応じて設定を見直してください。
これでgraphqlを使う準備はできました。djangoを立ち上げlocalhost:8000/api/graphql/
を叩くことで、graphqlのApolloクライアントが立ち上がります。
7. 実行する ( hello, world )#
mutations.py
には、次のようにmutationを書きます。
import strawberry
from strawberry.types import Info
from strawberry.django.context import StrawberryDjangoContext
@strawberry.type
class Mutation:
@strawberry.mutation
def hello(self, info: Info[StrawberryDjangoContext, None],) -> str:
return "Hello from imfaxblog!"
queries.pyにもqueryを追加します。
import strawberry
from strawberry.types import Info
from strawberry.django.context import StrawberryDjangoContext
@strawberry.type
class Query:
@strawberry.field()
def hello(self, info: Info[StrawberryDjangoContext, None],) -> str:
return "Hello from imfaxblog!"
新しくquery, mutationを書きたいときはメソッドを追加すればいいだけとなります。
さて、以下のgraphqlでメッセージが出ます。
query Query {
hello
}
mutation Mutation {
hello
}
以上で、Djangoでstrawberryを用いたGraphqlサーバーの完成となります。
Infoについて#
Info は、GraphQL リゾルバー関数 (ここでは hello 関数) に渡される引数の一つで、リクエストに関する様々な情報 (コンテキスト、フィールド情報など) を保持しています。
今回の例では、ルートオブジェクトを使用していないため、None を指定しています。つまり、Info[StrawberryDjangoContext, None] は、Info 型のジェネリクス (Generics) を用いて、Info が保持するコンテキストの型を StrawberryDjangoContext、ルートの型を None に指定という意味になります。
ここでのStrawberryDjangoContextは、 Django のリクエストコンテキスト (リクエスト、ユーザー情報など) を Strawberry で利用するための型です。info.context を通じて、Django のリクエストオブジェクトにアクセスできます。
Strawberryの本家でも infoを info: strawberry.Info
としているところがありますが、この場合だとinfoはAnyになり、リゾルバー関数内で info オブジェクトを通して利用できる情報が不明確になり、型チェッカーによる静的解析ができません。
補足説明: 任意の型を返すとき#
auth認証のある場合、userType を返したいという時の例を紹介します。
次のように userType を作り返却するだけです。
import strawberry
from strawberry.django.context import StrawberryDjangoContext
from someapp.your_custom_user_model import User # 大体の場合、ユーザはカスタムユーザーを使っているともいます
@strawberry.type # djangoの@strawberry_django.typeデコレータではなくても問題ないです。
class UserType:
id: int
username: str
age: int
@strawberry.type
class Mutation:
@strawberry.mutation(permission_classes=[IsAuthenticated]) # IsAuthenticatedはhttps://strawberry.rocks/docs/guides/permissionsを参照
def update_user_age(self, info: Info[StrawberryDjangoContext, None], age: int) -> UserType:
user = cast(User, info.context.request.user) # 後述
...
return UserType(id=user.id, username=user.username, age=age)
Auth 処理をしているので、info.context.request.user には user オブジェクトが入ることになります。 上記例ではpermission_classesを利用することで、問題があったときは例外が発生することになります。
ただ、sentryなど使っていると例外の発生は困る場合もあります。そういうときはextensionを使います。 詳しいやり方についてはこちらをご覧ください
castについて#
cast は、Python の型ヒント (Type Hinting) を利用して、変数の型を明示的に指定する関数です。info.context.request.user
は、実行時にDjangoの User オブジェクトであることが期待されますが、型チェッカー (例: MyPy) は、そのことを自動的に認識できません。そこでcast
を用いて型チェッカーに対して info.context.request.user
をUser型として扱うように指示します。
これにより、型チェッカーは、user変数に対して User型の属性やメソッドを安全に利用できることを確認できます。また型ヒントはコードの可読性を向上させ、開発者の意図を明確にする役割も果たします。
まとめ#
この記事では、DjangoでGraphQL API を開発するために、Strawberry を用いた基本的な設定とスキーマ設計について解説しました。
GraphQL の導入は、API 開発の効率化と柔軟性を高める上で非常に有効です。Strawberry を使うことで、Python らしい簡潔なコードで GraphQL API を実装できます。
この記事が、Django × Strawberry GraphQL の入門の一助となれば幸いです。