Pythonで始めるGraphQL【Strawberry】
はじめに
今回はStrawberryというライブラリを使って、PythonでGraphQLについて紹介したいと思います。
GraphQLは、FacebookによってRESTの課題を解決するために開発されたクエリ言語です。
具体的には以下のようなことができます。
特定のフィールドだけ取得
Bookオブジェクトはtitle, autherというフィールドを持っているとします。
この時autherが不要なら、Bookのtitle一覧のみ取得することができます。
ネストしたフィールドの取得
Userオブジェクトはid, name, follow_idsというフィールドを持っているとします。
idに基づきUserのnameを取得して、さらにfollow_idsに基づきフォローしているUserのnameを取得できます。
必要な情報を必要な分だけ取り出せるのがGraphQLの特徴ということになります。
もちろん、REST APIでもサーバー側でエンドポイントを作れば同じようなことができますが、GraphQLではエンドポイントを増やすことなくクライアント側でそれができるのがメリットです。
また、図中のGraphQLサーバーとデータベースの間に書かれているように、実際のデータの取得はGraphQLサーバーでプログラムする必要があります。
あくまで、GraphQLサーバーはクエリを解釈してデータを返すものになっています。
実行環境の作成
Getting startedに従って環境を作ります。
プロジェクトの準備
まず、新しいプロジェクトディレクトリを作成します。
mkdir strawberry-demo
cd strawberry-demo
仮想環境とライブラリのインストール
お好みの環境管理ツールを使用して、仮想環境を作成し、Strawberryをインストールします。
# pipの場合
pip install 'strawberry-graphql[debug-server]'
# poetryの場合
poetry add 'strawberry-graphql[debug-server]'
# uvの場合
uv add 'strawberry-graphql[debug-server]'
最初のGraphQLスキーマ
GraphQLの基本的な概念を理解するため、シンプルなヘルスチェックAPIを実装してみましょう。
スキーマの定義
schema.py
を作成し、以下のコードを記述します。
import strawberry
def get_status():
"""ステータスコード200を返すリゾルバー関数"""
return "200"
@strawberry.type
class Query:
"""クエリのルート型定義"""
status: str = strawberry.field(resolver=get_status)
schema = strawberry.Schema(query=Query)
ローカルサーバーの起動
スキーマを定義したら、開発用サーバーを起動します。
strawberry server schema
# Running strawberry on <http://0.0.0.0:8000/graphql> 🍓
GraphQLプレイグラウンド
この状態でブラウザでhttp://localhost:8000/graphql
を開くと、GraphQLプレイグラウンドが表示されます。
このプレイグラウンドでは、クエリを実行して試すことができます。
クエリの実行
作成したヘルスチェックAPIを試してみましょう。
基本的なクエリ
ブラウザで以下のクエリを入力し、実行ボタンを押すかCtrl+Enter
で実行します。
query {
status
}
サーバーからのレスポンス
{
"data": {
"status": "200"
}
}
実行結果のスクリーンショット
GraphQLの主要コンポーネント
GraphQLスキーマは、以下の3つの主要なコンポーネントで構成されています。先ほどのコードをもとに
、それぞれの役割と実装方法を詳しく見ていきましょう。
1. Query
@strawberry.type
class Query:
status: str = strawberry.field(resolver=get_status)
Queryは以下のFieldを集めたものになります。
2. Field
status: str = strawberry.field(resolver=get_status)
FieldはGraphQLサーバーが返すことのできるものになります。この場合は、status
というstrを返すことができます。
3. Resolver
def get_status() -> str:
"""ステータスコードを返すリゾルバー関数"""
return "200"
ResolverはFieldが指定されたときに、実際にどのような値を返すか決める関数になります。この場合は、固定でステータスコード"200"
を返しています。
コンポーネント間の関係
以下の図は、これらのコンポーネントの連携を示しています。
オブジェクトタイプ
オブジェクトを使用して値を返すこともできます。
ステータスコードだけではなくメッセージも追加したTypeを作成します。
先ほどのschema.py
に追記していきます。
まずは、ヘルスチェック用のTypeを定義します。これは出力の型になります。
@strawberry.type
class HealthCheck:
status: str
message: str
続いて、HealthCheck
Typeのリゾルバを定義します。
def get_health():
return HealthCheck(status="200", message="ok")
Query
のFieldにhealth_check
を追加します。
@strawberry.type
class Query:
# status: str = ...
health_check: HealthCheck = strawberry.field(resolver=get_health)
完全なコード
import strawberry
def get_status():
return "200"
@strawberry.type
class HealthCheck:
status: str
message: str
def get_health():
return HealthCheck(status="200", message="ok")
@strawberry.type
class Query:
status: str = strawberry.field(resolver=get_status)
health_check: HealthCheck = strawberry.field(resolver=get_health)
schema = strawberry.Schema(query=Query)
以下のようにクエリを入力して実行します。
ヘルスチェックのクエリを実行してみましょう。
query {
healthCheck{
status
message
}
}
サーバーからのレスポンス
{
"data": {
"healthCheck": {
"status": "200",
"message": "ok"
}
}
}
フィールドの選択
GraphQLではREST APIとは異なり、クライアント側で取得する内容を選択することができます。
例えば、message
だけ取得したい場合は以下のようにクエリを入力して実行します。
フィールドを選択して実行してみましょう。
query {
healthCheck{
message
}
}
サーバーからのレスポンス
{
"data": {
"healthCheck": {
"message": "ok"
}
}
}
なお、フィールドを1つも選択しないことはできません。
フィールドを選択せずに実行してみると、以下のようなエラーが発生します。
query {
healthCheck
}
サーバーからのエラーレスポンス
{
"data": null,
"errors": [
{
"message": "Field 'healthCheck' of type 'HealthCheck!' must have a selection of subfields. Did you mean 'healthCheck { ... }'?",
"locations": [
{
"line": 2,
"column": 3
}
]
}
]
}
このエラーは、オブジェクトタイプのフィールドに対して、取得したいフィールドを少なくとも1つは指定する必要があることを示しています。
ネストしたオブジェクトタイプ
ネストしたオブジェクトも定義することができます。
info
フィールドを追加したHealthCheckWithInfo
Typeを作成します。
まずはInfo
TypeとResolverを定義します。
@strawberry.type
class Info:
version: str
description: str
def get_info():
return Info(version="1.0", description="This is a health check")
HealthCheckWithInfo
Typeを作成します。
info
フィールドはリゾルバーを使って解決するようにします。
@strawberry.type
class HealthCheckWithInfo:
status: str
message: str
info: Info = strawberry.field(resolver=get_info)
HealthCheckWithInfo
のリゾルバーではinfoは指定しなくてよいです。
def get_health_with_info():
return HealthCheckWithInfo(status="200", message="ok")
完全なコード
import strawberry
def get_status():
return "200"
@strawberry.type
class HealthCheck:
status: str
message: str
def get_health():
return HealthCheck(status="200", message="ok")
@strawberry.type
class Info:
version: str
description: str
def get_info():
return Info(version="1.0", description="This is a health check")
@strawberry.type
class HealthCheckWithInfo:
status: str
message: str
info: Info = strawberry.field(resolver=get_info)
def get_health_with_info():
return HealthCheckWithInfo(status="200", message="ok")
@strawberry.type
class Query:
status: str = strawberry.field(resolver=get_status)
health_check: HealthCheck = strawberry.field(resolver=get_health)
health_check_with_info: HealthCheckWithInfo = strawberry.field(
resolver=get_health_with_info
)
schema = strawberry.Schema(query=Query)
以下のようにクエリを入力して実行します。
ネストしたオブジェクトを含むクエリを実行してみましょう。
query {
healthCheckWithInfo{
status
message
info { # ネストしたInfoオブジェクト
version
description
}
}
}
サーバーからのレスポンス
{
"data": {
"healthCheckWithInfo": {
"status": "200",
"message": "ok",
"info": {
"version": "1.0",
"description": "This is a health check"
}
}
}
}
ネストしていてもフィールドの選択は同じようにできます。
特定のフィールドのみを選択して実行してみましょう。
query {
healthCheckWithInfo{
message # メッセージのみ
info { # infoオブジェクトから
description # descriptionのみを選択
}
}
}
サーバーからのレスポンス
{
"data": {
"healthCheckWithInfo": {
"message": "ok",
"info": {
"description": "This is a health check"
}
}
}
}
このように、必要なフィールドのみを選択的に取得できます。
GraphQLによるCRUD操作の実装
データモデリングとスキーマ設計
GraphQLでのCRUD(Create, Read, Update, Delete)操作を、書籍管理システムを例に実装していきます。
データモデルの定義
まず、book
ディレクトリにschema.py
を作成し、基本的なデータ構造を定義します。
from datetime import datetime
import strawberry
# サンプルデータ
book_list = [
{
"title": "The Great Gatsby",
"author": "F. Scott Fitzgerald",
"publication_date": datetime(1925, 4, 10),
},
{
"title": "Jurassic Park",
"author": "Michael Crichton",
"publication_date": datetime(1990, 11, 20),
},
]
@strawberry.type
class Book:
"""書籍を表すGraphQLオブジェクトタイプ"""
title: str
author: str
publication_date: datetime
def list_books() -> list[Book]:
"""全書籍を取得するリゾルバー"""
return [Book(**book) for book in book_list]
@strawberry.type
class Query:
"""クエリのルート型"""
list_books: list[Book] = strawberry.field(
resolver=list_books,
description="全書籍のリストを取得"
)
# スキーマの定義
schema = strawberry.Schema(query=Query)
今回はQueryのフィールドとして、Bookタイプを使うのではなくそのリストとしています。
リストの場合でも、Bookのフィールドを指定してクエリします。
query {
listBooks{
title
author
publicationDate
}
}
{
"data": {
"listBooks": [
{
"title": "The Great Gatsby",
"author": "F. Scott Fitzgerald",
"publicationDate": "1925-04-10T00:00:00"
},
{
"title": "Jurassic Park",
"author": "Michael Crichton",
"publicationDate": "1990-11-20T00:00:00"
}
]
}
}
必要なフィールドのみを選択して取得することもできます。
query {
listBooks{
title # タイトルのみを取得
}
}
サーバーからのレスポンス
{
"data": {
"listBooks": [
{
"title": "The Great Gatsby"
},
{
"title": "Jurassic Park"
}
]
}
}
このように、必要なフィールドだけを指定することで、効率的なデータ取得が可能です。
引数の使用
タイトルを使用してBook
を取得するgetBook
フィールドを作成します。
そのために引数のあるリゾルバーを使用します。
def get_book(title: str):
"""タイトルに一致する書籍を取得するリゾルバー"""
return next(Book(**book) for book in book_list if book["title"] == title)
@strawberry.type
class Query:
list_books: list[Book] = strawberry.field(resolver=list_books)
get_book: Book = strawberry.field(resolver=get_book)
タイトルを指定して特定の書籍を取得してみましょう。
query {
book(title: "The Great Gatsby"){ # タイトルで書籍を指定
title # 取得したいフィールド
author
publicationDate
}
}
サーバーからのレスポンス
{
"data": {
"book": {
"title": "The Great Gatsby",
"author": "F. Scott Fitzgerald",
"publicationDate": "1925-04-10T00:00:00"
}
}
}
このように、引数を使用して特定のデータを取得できます。
データ更新操作の実装
GraphQLでは、データの更新操作をMutation
型として定義します。使い方としてはほとんどQuery
と変わりません。
まず、書籍を追加するリゾルバーを定義します。
def add_book(title: str, author: str, publication_date: datetime) -> Book:
"""新しい書籍を追加するリゾルバー
Args:
title: 書籍のタイトル
author: 著者名
publication_date: 出版日
Returns:
Book: 作成された書籍オブジェクト(推奨)
"""
book = Book(title=title, author=author, publication_date=publication_date)
book_list.append(vars(book))
return book
次に、Mutation型を定義します。
@strawberry.type
class Mutation:
"""データ更新操作のルート型"""
add_book: Book = strawberry.mutation(
resolver=add_book,
description="新しい書籍を追加"
)
最後に、スキーマにMutationを追加します。
schema = strawberry.Schema(
query=Query,
mutation=Mutation,
description="書籍管理システムのGraphQLスキーマ"
)
完全なコード
from datetime import datetime
import strawberry
book_list = [
{
"title": "The Great Gatsby",
"author": "F. Scott Fitzgerald",
"publication_date": datetime(1925, 4, 10),
},
{
"title": "Jurassic Park",
"author": "Michael Crichton",
"publication_date": datetime(1990, 11, 20),
},
]
@strawberry.type
class Book:
title: str
author: str
publication_date: datetime
def get_book(title: str):
return next(Book(**book) for book in book_list if book["title"] == title)
def list_books():
return [Book(**book) for book in book_list]
@strawberry.type
class Query:
list_books: list[Book] = strawberry.field(resolver=list_books)
get_book: Book = strawberry.field(resolver=get_book)
def add_book(title: str, author: str, publication_date: datetime) -> Book:
book = Book(title=title, author=author, publication_date=publication_date)
book_list.append(vars(book))
return book
@strawberry.type
class Mutation:
add_book: Book = strawberry.mutation(resolver=add_book)
schema = strawberry.Schema(query=Query, mutation=Mutation)
新しい書籍を追加してみましょう。
mutation {
addBook( # 書籍追加のMutation
title: "Little Prince", # 書籍のタイトル
author: "Antoine de Saint-Exupéry", # 著者名
publicationDate: "1943-04-01T00:00:00" # 出版日
) {
title # 追加した書籍の情報を取得
author
publicationDate
}
}
サーバーからのレスポンス
{
"data": {
"addBook": {
"title": "Little Prince",
"author": "Antoine de Saint-Exupéry",
"publicationDate": "1943-04-01T00:00:00"
}
}
}
このように、Mutationを使用してデータを追加できます。
なお、GraphQLではデータの更新後はそのオブジェクトを返すことがベストプラクティスとされています。
念のため、リストを確認しましょう。
query {
listBooks{
title
author
publicationDate
}
}
{
"data": {
"listBooks": [
{
"title": "The Great Gatsby",
"author": "F. Scott Fitzgerald",
"publicationDate": "1925-04-10T00:00:00"
},
{
"title": "Jurassic Park",
"author": "Michael Crichton",
"publicationDate": "1990-11-20T00:00:00"
},
{
"title": "Little Prince",
"author": "Antoine de Saint-Exupéry",
"publicationDate": "1943-04-01T00:00:00"
}
]
}
}
同じようにして、削除・更新も実装することができます。簡単なのでやってみてください。
完全なコード
from datetime import datetime
import strawberry
book_list = [
{
"title": "The Great Gatsby",
"author": "F. Scott Fitzgerald",
"publication_date": datetime(1925, 4, 10),
},
{
"title": "Jurassic Park",
"author": "Michael Crichton",
"publication_date": datetime(1990, 11, 20),
},
]
@strawberry.type
class Book:
title: str
author: str
publication_date: datetime
def get_book(title: str):
return next(Book(**book) for book in book_list if book["title"] == title)
def list_books():
return [Book(**book) for book in book_list]
@strawberry.type
class Query:
list_books: list[Book] = strawberry.field(resolver=list_books)
get_book: Book = strawberry.field(resolver=get_book)
def add_book(title: str, author: str, publication_date: datetime) -> Book:
book = Book(title=title, author=author, publication_date=publication_date)
book_list.append(vars(book))
return book
def remove_book(title: str) -> Book:
book = next(book for book in book_list if book["title"] == title)
book_list.remove(book)
return Book(**book)
def update_book(title: str, author: str, publication_date: datetime) -> Book:
book = next(book for book in book_list if book["title"] == title)
book["author"] = author
book["publication_date"] = publication_date
return Book(**book)
@strawberry.type
class Mutation:
add_book: Book = strawberry.mutation(resolver=add_book)
remove_book: Book = strawberry.mutation(resolver=remove_book)
update_book: Book = strawberry.mutation(resolver=update_book)
schema = strawberry.Schema(query=Query, mutation=Mutation)
ここで見たように、QueryとMutationは本質的に違いはありません。Queryのリゾルバーに削除処理を入れることも可能です。しかし、クライアントが使いやすいAPIになるように参照系はQuery、それ以外はMutationと分けることが、GraphQLのベストプラクティスとされています。
応用的なクエリ
User
を扱うAPIを例にGraphQLでの応用的なクエリを扱っていきます。
ここからはuser
ディレクトリでschema.py
を作成して作業していきます。
ローカルサーバーはstrawberry server user.schema
で起動します。
import strawberry
user_list = [
{"id": 1, "name": "Alice", "favorite": "music", "follow_ids": [2, 3, 4]},
{"id": 2, "name": "Bob", "favorite": "reading", "follow_ids": []},
{"id": 3, "name": "Charlie", "favorite": "traveling", "follow_ids": [2, 4]},
{"id": 4, "name": "David", "favorite": "cooking", "follow_ids": [1]},
{"id": 5, "name": "Eva", "favorite": "movies", "follow_ids": [1, 3]},
{"id": 6, "name": "Frank", "favorite": "gaming", "follow_ids": []},
]
@strawberry.type
class User:
id: int
name: str
favorite: str
@staticmethod
def get_user(user_id: int):
user = next(user for user in user_list if user["id"] == user_id)
return User(
id=user["id"],
name=user["name"],
favorite=user["favorite"],
)
@strawberry.type
class Query:
@strawberry.field
def get_user(self, user_id: int) -> User:
return User.get_user(user_id)
schema = strawberry.Schema(query=Query)
user_idをもとにUserを取得するようになっています。
今までと異なる点として、フィールドをメソッドで定義しています。
@strawberry.type
class Query:
@strawberry.field
def get_user(self, user_id: int) -> User:
# ここに処理が書ける
return User.get_user(user_id)
# 今までの記法
get_user: User = strawberry.field(resolver=User.get_user(user_id))
この記法ではメソッド自体がリゾルバーになっているような形で、処理を差し込みやすいです。
自身のフィールドを使ったフィールド
Userのfavoriteからシェア用の文言を返すフィールドを作ってみたいと思います。
@strawberry.type
class UserWithShere:
id: int
name: str
favorite: str
@strawberry.field
def share(self) -> str:
return f"{self.name} likes {self.favorite}, follow me!"
@staticmethod
def get_user(user_id: int):
user = next(user for user in user_list if user["id"] == user_id)
return UserWithShere(
id=user["id"],
name=user["name"],
favorite=user["favorite"],
)
@strawberry.type
class Query:
@strawberry.field
def get_user_with_shere(self, user_id: int) -> UserWithShere:
return UserWithShere.get_user(user_id)
selfを使うことで自身のフィールドを使用することができます。
クエリを実行してみましょう。
query {
getUserWithShere(userId: 1){
id
name
share
}
}
サーバーからのレスポンス
{
"data": {
"getUserWithShere": {
"id": 1,
"name": "Alice",
"share": "Alice likes music, follow me!"
}
}
}
自身を返すフィールド
https://strawberry.rocks/docs/guides/accessing-parent-data
follow_idsを使ってフォロワーを返すようなフィールドを作ってみます。
また、そのフォロワーのリストもユーザーのタイプを持たせたいと思います。
@strawberry.type
class UserWithFollows:
id: int
name: str
favorite: str
follow_ids: list[int]
@strawberry.field
def follows(self) -> list["UserWithFollows"]:
print(type(self))
return get_follows(self)
@staticmethod
def get_user(user_id: int):
user = next(user for user in user_list if user["id"] == user_id)
return UserWithFollows(
id=user["id"],
name=user["name"],
favorite=user["favorite"],
follow_ids=user["follow_ids"],
)
@strawberry.type
class Query:
@strawberry.field
def get_user_with_follows(self, user_id: int) -> UserWithFollows:
return UserWithFollows.get_user(user_id)
まず、基本的なフォロー関係を取得してみましょう。
query {
getUserWithFollows(userId: 1){
id
name
follows {
id
name
}
}
}
サーバーからのレスポンス
{
"data": {
"getUserWithFollows": {
"id": 1,
"name": "Alice",
"follows": [
{
"id": 2,
"name": "Bob"
},
{
"id": 3,
"name": "Charlie"
},
{
"id": 4,
"name": "David"
}
]
}
}
}
次に、フォロー関係を再帰的に取得してみましょう。
query {
getUserWithFollows(userId: 1){
id
name
follows {
id
name
follows {
id
name
}
}
}
}
サーバーからのレスポンス
{
"data": {
"getUserWithFollows": {
"id": 1,
"name": "Alice",
"follows": [
{
"id": 2,
"name": "Bob",
"follows": []
},
{
"id": 3,
"name": "Charlie",
"follows": [
{
"id": 2,
"name": "Bob"
},
{
"id": 4,
"name": "David"
}
]
},
{
"id": 4,
"name": "David",
"follows": [
{
"id": 1,
"name": "Alice"
}
]
}
]
}
}
}
循環参照するフィールド
複雑なデータ関係を持つAPIを実装する際、循環参照の問題が発生することがあります。ここでは、ソーシャルメディアの投稿システムを例に、その解決方法を見ていきましょう。
まず、投稿データの基本構造を定義します。
# サンプルデータ:投稿リスト
post_list = [
{
"id": 1,
"content": "What a beautiful day today!",
"user_id": 1,
"liked_by_ids": [2, 3], # いいねしたユーザーのID
},
{
"id": 2,
"content": "Started reading a new book",
"user_id": 2,
"liked_by_ids": [1],
},
{
"id": 3,
"content": "Found a great restaurant",
"user_id": 3,
"liked_by_ids": [1, 2, 4],
},
]
投稿(Post)には以下の要素が含まれます。
フィールド | 型 | 説明 |
---|---|---|
id | int | 投稿の一意識別子 |
content | str | 投稿内容 |
user | User | 投稿者への参照 |
liked_by | User[] | いいねしたユーザーのリスト |
このような相互参照を含むモデルでは、以下のような循環参照が発生します。
以下のように、循環参照を考慮した実装を行います。
@strawberry.type
class Post:
"""投稿を表すGraphQLオブジェクトタイプ"""
id: int
content: str
# 文字列で型を参照することで循環参照を回避
user: "UserWithPosts" = strawberry.field(
resolver=lambda self: UserWithPosts.get_user(self.user_id),
description="投稿者の情報"
)
liked_by: list["UserWithPosts"] = strawberry.field(
resolver=lambda self: [UserWithPosts.get_user(user_id) for user_id in self.liked_by_ids],
description="いいねしたユーザーのリスト"
)
@strawberry.field(description="いいねの総数")
def like_count(self) -> int:
"""いいねの数を返す計算フィールド"""
return len(self.liked_by_ids)
def __init__(self, **kwargs):
"""投稿オブジェクトの初期化
Args:
kwargs: 投稿データの辞書
- id: 投稿ID
- content: 投稿内容
- user_id: 投稿者のID
- liked_by_ids: いいねしたユーザーのIDリスト
"""
self.id = kwargs["id"]
self.content = kwargs["content"]
self.user_id = kwargs["user_id"]
self.liked_by_ids = kwargs["liked_by_ids"]
@strawberry.type
class UserWithPosts:
"""投稿情報を含むユーザーを表すGraphQLオブジェクトタイプ"""
id: int
name: str
favorite: str
@strawberry.field(description="ユーザーの投稿一覧")
def posts(self) -> list[Post]:
"""ユーザーが作成した投稿を取得
Returns:
list[Post]: ユーザーの投稿リスト
"""
return [Post(**post) for post in post_list if post["user_id"] == self.id]
@strawberry.field(description="ユーザーがいいねした投稿一覧")
def liked_posts(self) -> list[Post]:
"""ユーザーがいいねした投稿を取得
Returns:
list[Post]: いいねした投稿のリスト
"""
return [Post(**post) for post in post_list if self.id in post["liked_by_ids"]]
@staticmethod
def get_user(user_id: int) -> "UserWithPosts":
"""ユーザーIDからユーザー情報を取得
Args:
user_id: 取得対象のユーザーID
Returns:
UserWithPosts: ユーザー情報
"""
user = next(user for user in user_list if user["id"] == user_id)
return UserWithPosts(
id=user["id"],
name=user["name"],
favorite=user["favorite"],
)
@strawberry.type
class Query:
"""ソーシャルメディアAPIのクエリルート"""
@strawberry.field(
description="ユーザーと投稿情報を取得",
)
def get_user_with_posts(self, user_id: int) -> UserWithPosts:
"""指定されたIDのユーザーと、その投稿情報を取得
Args:
user_id: 取得対象のユーザーID
Returns:
UserWithPosts: ユーザーと関連する投稿情報
"""
return UserWithPosts.get_user(user_id)
# スキーマの定義
schema = strawberry.Schema(
query=Query,
description="ソーシャルメディアAPIのGraphQLスキーマ",
types=[Post, UserWithPosts] # 明示的に型を登録
)
ユーザーの投稿情報と関連データを取得する複雑なクエリを実行してみましょう。
query GetUserWithPosts {
getUserWithPosts(userId: 1) {
id # ユーザーID
name # ユーザー名
posts { # ユーザーの投稿一覧
id
content
likeCount # いいねの数(計算フィールド)
likedBy { # いいねしたユーザー
id
name
posts { # いいねしたユーザーの投稿
id
content
}
}
}
likedPosts { # ユーザーがいいねした投稿
id
content
user { # 投稿者の情報
id
name
}
}
}
}
サーバーからのレスポンス
{
"data": {
"getUserWithPosts": {
"id": 1,
"name": "Alice",
"posts": [
{
"id": 1,
"content": "What a beautiful day today!",
"likeCount": 2,
"likedBy": [
{
"id": 2,
"name": "Bob",
"posts": [
{
"id": 2,
"content": "Started reading a new book"
}
]
},
{
"id": 3,
"name": "Charlie",
"posts": [
{
"id": 3,
"content": "Found a great restaurant"
}
]
}
]
}
],
"likedPosts": [
{
"id": 2,
"content": "Started reading a new book",
"user": {
"id": 2,
"name": "Bob"
}
},
{
"id": 3,
"content": "Found a great restaurant",
"user": {
"id": 3,
"name": "Charlie"
}
}
]
}
}
}
実際にはリゾルバーで効率的にデータを取得するために、データローダーを使うなどの工夫が必要ですが、このような複雑なフィールドも取得することができます。
おわりに
この記事では、StrawberryによるGraphQL APIの実装について見てきました。
GraphQLのメリットや使い方が伝われば幸いです。
参考
Discussion