🌰

基礎から学ぶ GraphQL

に公開

はじめに

従来の REST API には「複数のエンドポイントを呼び出す必要がある」「不要なデータまで取得してしまう」といった課題がありました。
GraphQL は、クライアントが必要なデータを柔軟に取得できる API のためのクエリ言語です。
本記事では、その基本から実際の実装方法までを学んでいきましょう。

GraphQL (グラフキューエル) とは?

GraphQL は、Facebook(現 Meta)が開発し公開した API のための新しい仕組みです。
従来の REST API では「このエンドポイントを叩くと、このデータが返ってくる」と決まっていましたが、GraphQL ではクライアントが欲しいデータをリクエストで指定できるのが大きな特徴です。

また、GraphQL ではスキーマによってデータの型があらかじめ定義されているため、「このフィールドは文字列なのか数値なのか」「この項目は必須かどうか」といった情報が明確になります。
そのおかげで、フロントエンドやバックエンドでの開発時に型チェックが効きやすく、ミスを防ぎながら安全に開発できるのも特徴です。

スキーマ (Schema)

GraphQL では、API のデータ構造を「スキーマ」として定義します。
スキーマは、データの型や関係性を記述するもので、API の仕様書のような役割を果たします。

例えば、ユーザー情報を扱うスキーマは以下のようになります。

type User {
    id: ID!
    name: String!
    email: String!
    posts: [Post!]!
}
type Post {
    id: ID!
    title: String!
    content: String!
}
type Query {
    user(id: ID!): User
    users: [User!]!
    post(id: ID!): Post
}

クエリ (Query)

クエリは、データを読み取るための操作です。
スキーマに基づいて必要なデータを指定してリクエストを送ります。

特定のユーザーの名前とメールアドレスを取得するクエリは以下のようになります。

# 特定のユーザーの名前とメールアドレスを取得するクエリ
query {
    user(id: "1") {
        name
        email
    }
}

# 関連する投稿も含めて取得するクエリ
query {
    user(id: "1") {
        name
        email
        posts {
            title
            content
        }
    }
}

ミューテーション (Mutation)

ミューテーションは、データを作成、更新、削除するための操作です。

ユーザーを作成・更新する場合は以下のようになります。

# 新しいユーザーを作成するミューテーション
mutation {
    createUser(
        name: "Tanaka Taro",
        email: "tanaka@example.com"
    ) {
        id
        name
        email
    }
}

サブスクリプション (Subscription)

サブスクリプションは、リアルタイムでデータの変更を受け取るための操作です。

以下は新しい投稿が追加されたときに通知を受け取る場合の例です。

subscription {
    postAdded {
        id
        title
        content
    }
}

REST APIとの違い

次に、REST API と比較しながら GraphQL の特徴を見ていきましょう。

REST API

  • 複数のエンドポイント(/users、/posts、/comments など)
  • 固定されたデータ構造
  • 複数回のリクエストが必要な場合がある

GraphQL

  • 単一のエンドポイント
  • クライアントが必要なデータを指定
  • 1回のリクエストで関連データを取得可能

具体例で比較

GraphQL と REST API の違いを具体例で比較してみたいと思います。

例: ユーザーの情報(名前とメールアドレス)と、そのユーザーの投稿一覧(タイトル)を取得したい

REST API の場合

# 1. ユーザー情報の取得
GET /users/1
{
  "id": "1",
  "name": "田中太郎",
  "email": "tanaka@example.com",
  "age": 30,
  "address": "東京"
}
# 2. そのユーザーの投稿を取得
GET /users/1/posts
[
  { "id": "1", "title": "GraphQL入門", "content": "..." },
  { "id": "3", "title": "REST APIの基本", "content": "..." }
]

2回のリクエストが必要になり、不要なフィールド(age, address, content など)も返ってきます。

GraphQL の場合

query {
  user(id: "1") {
    name
    email
    posts {
      title
    }
  }
}
{
  "data": {
    "user": {
      "name": "田中太郎",
      "email": "tanaka@example.com",
      "posts": [
        { "title": "GraphQL入門" },
        { "title": "REST APIの基本" }
      ]
    }
  }
}

上記のように、GraphQL は「1回のリクエストで必要なデータだけを取得できる」という利点があります。
一方で、GraphQL にも以下のような注意点があります。

  • キャッシュが難しい
    REST のように URL 単位でキャッシュするのが難しく、キャッシュ戦略の設計が必要です。
  • 学習コスト
    スキーマ設計やリゾルバー実装など、REST よりも初期の学習コストは高めです。

違いをまとめ

改めて、REST API と GraphQL の違いを表にまとめると以下のようになります。

項目 REST API GraphQL
エンドポイント リソースごとに複数必要(/users, /posts など) 単一のエンドポイントで済む(例: /graphql)
データ取得 固定されたレスポンス形式 クライアントが欲しいフィールドを指定できる
データ量 不要なデータまで含まれることがある(Over-fetching) 必要なデータだけ取得できる
リクエスト回数 関連データ取得に複数回必要(例: ユーザーと投稿) 1回のクエリでまとめて取得可能
キャッシュ HTTP レベルのキャッシュを利用しやすい URL 単位でキャッシュするのが難しく、キャッシュ戦略の設計が必要
リアルタイム通信 標準ではサポートなし(WebSocket など追加実装が必要) サブスクリプションで標準的にサポート

それぞれどんなケースに向いているかも見てみましょう。

REST API が向いているケース

  • シンプルな CRUD 操作が中心の API : リソースが明確で、操作が単純な場合に最適
  • 公開 API やサードパーティとの連携 : REST は Web の標準的な技術であるため、多くの開発者に馴染みがあり、理解しやすい
  • 既存システムとの互換性を重視 : 既存のシステムや外部 API の多くが REST ベースであるため、統合がスムーズ

GraphQL が向いているケース

  • 複雑なデータを扱う場合 : 複数のデータを1つのクエリで取得し、統合するのに優れている
  • クライアントごとに必要なデータが異なる場合 : モバイルと Web で異なるデータが必要な場合など、柔軟に対応できる
  • リアルタイムでの更新が必要 : チャットや通知などの双方向通信を扱う場合

実際に作ってみよう

次は Node.js と Apollo Server を使って、簡単な GraphQL サーバーを作ってみましょう。

プロジェクトのセットアップ

新しいディレクトリを作成し、必要なパッケージをインストールします。

npm init -y
npm install apollo-server-express express graphql

サーバーの実装

server.js ファイルを作成し、以下のコードを追加します。

// 1. 必要なモジュールのインポート
const { ApolloServer, gql } = require('apollo-server-express');
const express = require('express');

// 2. サンプルデータの定義
const users = [
    { id: '1', name: '田中太郎', email: 'tanaka@example.com', age: 30, address: '東京' },
    { id: '2', name: '佐藤花子', email: 'sato@example.com', age: 25, address: '大阪' },
    { id: '3', name: '鈴木次郎', email: 'suzuki@example.com', age: 28, address: '名古屋' },
];

const posts = [
    { id: '1', title: 'GraphQL入門', content: 'GraphQLについて...', authorId: '1', created_at: '2025-01-01 12:00:00' },
    { id: '2', title: 'React学習', content: 'Reactについて...', authorId: '2', created_at: '2025-01-05 14:00:00' },
    { id: '3', title: 'REST APIの基本', content: 'REST APIについて...', authorId: '1', created_at: '2025-01-08 13:00:00' },
];

// 3. スキーマ定義
const typeDefs = gql`
  type User {
    id: ID!
    name: String!
    email: String!
    posts: [Post!]!
  }

  type Post {
    id: ID!
    title: String!
    content: String!
    author: User!
  }

  type Query {
    users: [User!]!
    user(id: ID!): User
    posts: [Post!]!
    post(id: ID!): Post
  }

  type Mutation {
    createUser(name: String!, email: String!): User!
  }
`;

// 4. リゾルバー実装
// リゾルバー(Resolver)は、スキーマで定義した各フィールドに対して「実際にどのようにデータを処理するか」を実装する関数です。
// クライアントがクエリやミューテーションを送信すると、対応するリゾルバが実行され、データベースや他のデータソースとやり取りしてクライアントに結果を返します。
const resolvers = {
    Query: {
        users: () => users,
        user: (_, { id }) => users.find(user => user.id === id),
        posts: () => posts,
        post: (_, { id }) => posts.find(post => post.id === id)
    },

    User: {
        posts: (user) => posts.filter(post => post.authorId === user.id)
    },

    Post: {
        author: (post) => users.find(user => user.id === post.authorId)
    },

    Mutation: {
        createUser: (_, { name, email }) => {
            const newUser = {
                id: String(users.length + 1),
                name,
                email
            };
            users.push(newUser);
            return newUser;
        }
    }
};

// 5. サーバー起動
async function startServer() {
    const server = new ApolloServer({ typeDefs, resolvers });
    const app = express();

    await server.start();
    server.applyMiddleware({ app });

    app.listen({ port: 4000 }, () => {
        console.log(`🚀 Server ready at http://localhost:4000${server.graphqlPath}`);
    });
}

startServer();

サーバー起動

以下のコマンドでサーバーを起動します。

node server.js

Apollo Studio で試す

サーバーが起動したら、ブラウザで http://localhost:4000/graphql にアクセスします。
「Query your server」 ボタンをクリックすると、Apollo Studio Sandbox(https://studio.apollographql.com/sandbox/explorer)に遷移し、ローカルサーバーに接続された状態でクエリを実行できます。

遷移できたら、以下のようなクエリやミューテーションを試してみてください。

# 全ユーザー取得
query {
  users {
    id
    name
    email
    posts {
      title
    }
  }
}

# 特定ユーザーの詳細情報
query {
  user(id: "1") {
    name
    email
    posts {
      title
      content
    }
  }
}

# 新しいユーザー作成
mutation {
  createUser(name: "山田太郎", email: "yamada@example.com") {
    id
    name
    email
  }
}

以下画像のように、クエリを実行して結果が返ってくれば成功です。

おわりに

本記事では、GraphQL の基本概念から実際のサーバー実装まで学んできました。

GraphQL を使うことで、フロントエンドは必要なデータだけを柔軟に取得でき、複雑なデータ構造も1回のリクエストでまとめて取得できるようになります。
一方で、すべてのプロジェクトに最適というわけではなく、プロジェクトの要件や開発チームのスキル、既存システムとの兼ね合いを考慮して、適切な技術選択を行うことが重要です。

まずは小さく作って試すことから始めると、GraphQL の特徴や便利さを実感しやすいと思います。
ぜひ紹介した内容を参考に実際に手を動かして学んでみてください。

Discussion