近年のWebアプリケーションでは、画面構成やUXの多様化にともない「より柔軟で効率的なデータ取得」の重要性が高まっています。
特にフロントエンドでは、不要なデータ取得や複数回のAPIリクエストが開発体験やパフォーマンスに影響を与えるケースも増えてきました。
よく使われているREST APIでは、エンドポイントごとに決まったレスポンスが返ってくるため、 フロント側の要件に合わせて「不要なデータを受け取る」や「複数のエンドポイントを呼び分ける」 といった非効率が発生しがちです。
これらの課題を解決する手段として注目されているのがGraphQL
です。
GraphQLを使えば、クライアントが必要なデータだけを明確に指定して取得できるため、 通信の無駄が減り開発効率やパフォーマンスの向上につながります。
本記事では、Symfony と API Platformを使って、GraphQL APIを簡単に作成する方法を紹介します。
✅️ 今回作成するもの
今回は「書籍(Book)」と「レビュー(Review)」のデータを扱うGraphQL APIを作成します。
- 書籍一覧の取得
- 各書籍に紐づくレビューの平均スコアの取得
- React + Apollo Clientを使ったフロントエンドでのデータ取得
フロント以外のコードはGitHubに公開していますので、ぜひ参考にしてください。
🔗GitHub:symfony-api-platform-graphql
📝 目次
- 1. Symfonyプロジェクトのセットアップ
- 2. API Platformの導入
- 3. データベース接続(PostgreSQL)
- 4. Book / Reviewエンティティの作成とマイグレーション
- 5. データ登録 (REST API)
- 6. GraphQLの有効化
- 7. GraphQLで書籍一覧を取得
- 8. カスタムフィールド(レビューの平均スコア)を追加
- 9. フロント側からデータを取得する
- 10. まとめ
1. Symfonyプロジェクトのセットアップ
使用バージョン:
- PHP:
v8.2.28
- Symfony:
v7.3.1
まずは新規プロジェクトを作成します。
$ symfony new symfony-api-platform-graphql
2. API Platformの導入
API Platformは、PHP製のWeb APIフレームワークです。 導入するだけで自動的にRESTとGraphQLのAPIエンドポイントが作成されます。
$ composer require api
これだけで/api
にアクセスすると、Swagger UIでREST APIのドキュメントが確認できます。
💡 API Platform では、REST API と GraphQL API の両方を併用できます。 エンティティを定義するだけで、自動的に REST のエンドポイントが生成され、Swagger UI から確認や操作も可能です。 本記事では GraphQL をメインに扱いますが、動作確認も兼ねてデータ登録には REST API を使って進めていきます。
3. データベース接続(PostgreSQL)
.env
ファイルを編集し、PostgreSQLに接続できるように設定します。
- DATABASE_URL="postgresql://app:!ChangeMe!@127.0.0.1:5432/app?serverVersion=16&charset=utf8"
+ DATABASE_URL="postgresql://app:!@127.0.0.1:5432/symfony-api-platform-graphql?serverVersion=16&charset=utf8"
- ユーザー名やパスワードは自分の環境に合わせて変更してください。
- 事前にPostgreSQLをインストールしておく必要があります。
- PostgreSQL の代わりに SQLite や MySQL を使っても大丈夫です。
その後、以下のコマンドでデータベースを作成します。
$ php bin/console doctrine:database:create
4. Book / Reviewエンティティの作成とマイグレーション
まずは開発用にMakerBundleを追加します。
$ composer require symfony/maker-bundle --dev
次に、書籍(Book)
とレビュー(Review)
のエンティティを作成し、マイグレーションを実行します。
$ php bin/console make:entity
$ php bin/console make:migration
$ php bin/console doctrine:migrations:migrate
👇️️ エンティティ定義のコード
反映後、/api
にアクセスすると、Book
とReview
のエンドポイントが自動生成されていることが確認できます。
次に、実際にBookとReviewのデータをAPI経由で登録してみます。
5. データ登録 (REST API)
生成された/api
エンドポイントに対して、Postman
などを使ってデータを登録します。
Bookデータを登録するために、以下のように/api/books
にPOST
リクエストを送信します。
{
"isbn": "978-4-7741-8411-1",
"title": "ドメイン駆動設計入門",
"description": "DDDの基礎を学べる書籍です。",
"author": "山田太郎",
"publicationDate": "2024-05-01T00:00:00+09:00"
}
Reviewデータの登録も同様に/api/reviews
にPOST
リクエストを行います。
{
"rating": 5,
"body": "とても分かりやすい内容でDDDが理解できました!",
"author": "読者A",
"publicationDate": "2024-05-10T00:00:00+09:00",
"book": "/api/books/1"
}
データベースにデータが入っていることが確認できます。
サンプルデータとしてBook
とReview
データをいくつか登録しておきます。
サンプルデータはREADME.mdに記載しておきます。
REST APIが問題なく動作することを確認できたので、 ここからは本題であるGraphQLの利用方法を見ていきます。
6. GraphQLの有効化
GraphQLを有効化します。
$ composer require api-platform/graphql
これで/graphql
にアクセスすれば、GraphiQL(GraphQL IDE)が利用可能になります。
7. GraphQLで書籍一覧を取得
API Platformでは、GraphQLエンドポイント(/graphql
)が自動で用意されており、ブラウザからGraphiQLを使ってクエリを試すことができます。
まずは、シンプルに書籍の一覧を取得してみます。
query {
books {
edges {
node {
id
title
author
isbn
description
publicationDate
}
}
}
}
このように、GraphQLでは必要なフィールドだけを指定して取得できるのが大きなメリットです。
edges > node
という構造は、Relay仕様に沿ったページネーション形式です。
👇️️ 結果画面はこんな感じ
8. カスタムフィールド(レビューの平均スコア)を追加
クライアントでレビューを集計するのは手間なので、バックエンドで平均スコアを計算して返すようにします。
📌 Bookエンティティに集計ロジックを追加
#[Groups(['book:read'])]
public function getAverageRating(): ?float
{
if (!is_iterable($this->reviews) || count($this->reviews) === 0) {
return null;
}
$sum = 0;
$count = 0;
foreach ($this->reviews as $review) {
$sum += $review->rating;
$count++;
}
return $count > 0 ? round($sum / $count, 2) : null;
}
レビューが存在する場合だけ平均を計算し、小数第2位で丸めて返すというシンプルな実装です。
📌 GraphQLでフィールドを有効化する
このaverageRating
をGraphQLのレスポンスに含めるためには、ApiResource
にグループ指定を追加します。
#[ApiResource(normalizationContext: ['groups' => ['book:read']])]
これでGraphQL側でも以下のようなクエリで取得可能になります!
query {
books {
edges {
node {
title
averageRating
}
}
}
}
👇️️ 結果画面はこんな感じ
バックエンドで集計処理を済ませておけば、フロント側はaverageRating
をクエリに追加するだけで、すぐに表示できます。
REST APIでも集約済みのデータを1リクエストで取得することは可能ですが、 事前に専用のエンドポイントを用意する必要があり画面ごとのデータ要件に応じて設計が増えていきがちです。
その点GraphQLでは、クライアントが欲しい形で柔軟にクエリを定義できるため、バックエンド側は汎用的なスキーマを用意するだけで済みます。
その結果、クライアントは欲しいデータを、欲しい形で取得できるようになり、 バックエンド側も汎用的なスキーマ設計だけで済むため、API設計の手間を大きく減らすことができます。
GraphQL のこうした柔軟さは、複雑な画面構成や多様なデータ要件を持つアプリ開発において、特に効果を発揮します。
9. フロント側からデータを取得する
GraphQLの大きな利点のひとつが、「フロントエンドから必要なデータだけを効率的に取得できる」点です。
ここではReact + Apollo Client
を使って、書籍のタイトルと平均スコアを表示するシンプルなUIを作ってみます。
GraphQLクエリの定義
const GET_BOOKS = gql`
query {
books {
edges {
node {
id
title
averageRating
}
}
}
}
`;
コンポーネント側の実装
import { gql, useQuery } from '@apollo/client';
const BookList = () => {
const { loading, error, data } = useQuery(GET_BOOKS);
if (loading) return <p>読み込み中...</p>;
if (error) return <p>エラー: {error.message}</p>;
return (
<ul>
{data.books.edges.map(({ node }: any) => (
<li key={node.id}>
{node.title} - 平均スコア: {node.averageRating ?? '評価なし'}
</li>
))}
</ul>
);
};
export default BookList;
このように、GraphQLクエリを使ってフロント側で必要なフィールドだけを柔軟に取得できます。
バックエンド側でGroups
属性によってフィールド公開範囲を調整できるのも、API Platform の便利な点です。
10. まとめ
今回の構成(Symfony + API Platform + GraphQL)を振り返ってみると、こんな強みがありました。
- ✅
自動生成
:エンティティ定義だけでREST/GraphQL APIを即構築可能 - ✅
柔軟性
:Groups属性で公開フィールドを制御したり、フロントエンドから必要なデータを自由に取得できる - ✅
効率性
:GraphQL により、必要な情報だけを最小限で取得できるため、通信や処理の無駄がない
GraphQLが初めての方でも、API Platformを使えば簡単に構築できます。
特に、データの粒度を調整したい場面や複雑な画面構成を持つSPA開発においては、GraphQLのメリットを最大限に活かせます。
今回は実装中心の紹介でしたが、GraphQLはUIの多様化に柔軟に対応できる強力な選択肢です。
ぜひ一度、試してみてください!