shibomb

GraphQL vs REST API:どっちを選ぶ?2024年最新API設計ガイドで学ぶ、実践的ベストプラクティス

はじめに

こんにちは!プログラミングの世界へようこそ。バックエンド開発に足を踏み入れると、必ずと言っていいほど「API」という言葉に出会います。そして、その設計方法としてよく議論されるのが「GraphQL」と「REST API」です。

「どっちを使えばいいの?」「何が違うの?」と悩んでしまうのは、誰もが通る道です。僕も昔は、たくさんの記事を読んで混乱した経験があります。でも、大丈夫。この記事を読み終える頃には、あなたは両者の違いを明確に理解し、自分のプロジェクトにどちらが適しているかを自信を持って判断できるようになります。

このガイドでは、単なる技術の比較に終わりません。実際に手を動かしながら、それぞれのAPIを実装し、チーム開発でどう活かしていくかという実践的な視点まで踏み込みます。小さな成功体験を積み重ねながら、API設計の楽しさと奥深さを一緒に学んでいきましょう。あなたのエンジニアとしての新たな一歩を、全力で応援します!

前提知識の確認

本格的な内容に入る前に、少しだけ準備運動をしましょう。もし分からない言葉があっても、心配しないでください。今は「そんなものがあるんだな」と頭の片隅に置くだけで十分です。

必要な基礎知識

  • HTTPの基本: GET, POST といったメソッドが何をするものか、ステータスコード(200 OK, 404 Not Foundなど)が何を意味するかなんとなく分かっているとスムーズです。
  • JSON形式: JavaScriptのオブジェクトによく似た、データをやり取りするための書式です。{"key": "value"} のような形に見覚えがあれば問題ありません。
  • 基本的なプログラミング経験: JavaScript(特にNode.js)の基本的な文法が分かっていると、コード例の理解が深まります。他の言語の経験でも大丈夫です。

事前に理解しておきたい概念

  • API (Application Programming Interface): ソフトウェアやプログラム同士が情報をやり取りするための「窓口」や「接点」のことです。例えば、天気予報アプリが気象庁のサーバーから天気データを取得する際にAPIを使っています。
  • クライアントとサーバー: 情報を「要求する側」(Webブラウザやスマホアプリ)をクライアント、情報を「提供する側」をサーバーと呼びます。APIは、この二者の間のコミュニケーションルールを定めたものです。

「分からなくても大丈夫」な部分

  • 複雑なネットワークの知識: TCP/IPなどの詳細なネットワークプロトコルの知識は、現時点では不要です。
  • データベースの専門知識: この記事では、データベースの代わりにシンプルなデータを使って説明します。SQLなどに詳しくなくても全く問題ありません。

焦らず、自分のペースで進んでいきましょう。一番大切なのは「やってみよう」という気持ちです。

環境構築:最初の一歩

理論を学ぶのも大切ですが、エンジニアリングの醍醐味はやはり手を動かすことです。ここでは、APIサーバーを動かすための最小限の環境を準備します。

開発環境の準備(初心者向け解説)

今回は、世界中で広く使われているJavaScriptの実行環境である「Node.js」を使います。Node.jsをインストールすると、「npm」というパッケージ管理ツールも一緒に使えるようになります。これを使えば、必要なライブラリ(便利なプログラムの部品)を簡単に導入できます。

必要なツールとインストール方法

  1. Node.jsのインストール: 公式サイトにアクセスし、LTS(Long Term Support)版をダウンロードしてインストールしてください。インストールが完了したら、ターミナル(WindowsならコマンドプロンプトやPowerShell)を開いて、以下のコマンドを実行します。

    node -v
    npm -v

    バージョン番号が表示されれば、インストールは成功です。

  2. プロジェクトフォルダの作成と初期化: 好きな場所にプロジェクト用のフォルダを作成し、そのフォルダに移動します。

    mkdir api-practice
    cd api-practice
    npm init -y

    npm init -y を実行すると、package.json というプロジェクト管理ファイルが作成されます。

  3. 必要なライブラリのインストール: Webサーバーを簡単に作るための「Express」、そしてGraphQLを扱うためのライブラリをインストールします。

    npm install express graphql express-graphql

これで準備は完了です。いよいよコーディングの時間です!

環境構築でつまずきやすいポイント

環境構築は、開発の最初にして最大の壁になることがあります。もし「コマンドが通らない」「エラーが出る」といった問題が起きても、慌てないでください。よくあるのは、Node.jsのバージョンが古い、コマンドの打ち間違い、権限の問題などです。エラーメッセージをよく読み、コピーして検索してみるのが解決への近道です。この試行錯誤も、エンジニアとしての大切な経験になります。

基本概念の理解

RESTとGraphQL、それぞれの「考え方の核」を理解することが、適切な技術選定の鍵となります。

核となる考え方

  • REST API: 「リソース(資源)」という考え方が中心です。例えば、「ユーザー情報」「商品情報」といったデータ一つ一つをリソースとして捉えます。そして、各リソースに対応するURL(エンドポイントと呼びます)を用意します。例えば、/usersで全ユーザーを、/users/1でIDが1のユーザーを取得する、といった具合です。操作はHTTPメソッド(GET, POST, PUT, DELETE)で表現します。

  • GraphQL: 「クライアントが必要なデータを、必要な分だけ、一度のリクエストで取得する」という考え方が中心です。エンドポイントは基本的に一つ(例えば /graphql)だけです。クライアントは、そのエンドポイントに対して「クエリ」と呼ばれる問い合わせ文を送り、サーバーはクエリの構造通りにデータを返します。

身近な例での説明

  • REST APIは「定食屋さん」 お店には「焼魚定食」「唐揚げ定食」といった決まったメニュー(エンドポイント)があります。お客さん(クライアント)はメニューから一つを選んで注文します。もし唐揚げ定食のご飯が不要でも、定食の一部として必ず付いてきてしまいます(オーバーフェッチ)。また、焼魚と唐揚げを少しずつ食べたい場合は、二つの定食を注文する必要があります(アンダーフェッチ)。

  • GraphQLは「アラカルト(単品注文)のレストラン」 お客さん(クライアント)は、一枚の注文票(クエリ)に「唐揚げを2個」「サラダを少し」「ご飯は半分」といったように、欲しいものを欲しい分だけ自由に書いて店員さん(サーバー)に渡します。すると、その注文通りの料理が一度に運ばれてきます。無駄がなく、非常に効率的です。

「なぜそうなるのか」の理解

RESTは、Webの仕組みであるHTTPを素直に利用したシンプルで分かりやすい設計思想から生まれました。長年にわたりWeb APIのスタンダードとして使われてきました。

定食屋さん(REST)とアラカルトレストラン(GraphQL)の対比を表すイラスト。それぞれのメリット・デメリットを視覚的に表現。

一方、GraphQLはFacebook(現Meta)がモバイルアプリ開発の課題を解決するために開発しました。モバイルアプリでは通信量が限られており、また様々な画面で異なるデータの組み合わせが必要になります。RESTのように何度も通信したり、不要なデータを受け取ったりするのは非効率でした。そこで、クライアントがデータの要求仕様を決められるGraphQLが生まれたのです。

実践編:手を動かして学ぶ

それでは、実際にコードを書いて、RESTとGraphQLの違いを体感してみましょう。index.jsというファイルを作成して、以下のコードを書いていきます。

まず、共通で使うサンプルデータを用意します。

// サンプルデータ
const users = [
  { id: '1', name: 'Taro Yamada', email: 'taro@example.com' },
  { id: '2', name: 'Hanako Suzuki', email: 'hanako@example.com' },
];

const posts = [
  { id: '101', userId: '1', title: 'REST API Introduction' },
  { id: '102', userId: '2', title: 'GraphQL for Beginners' },
  { id: '103', userId: '1', title: 'Advanced REST Techniques' },
];

ステップ1: 基本的な実装

REST APIの実装

Expressを使って、ユーザーリストと特定のユーザー情報を取得するエンドポイントを作成します。

const express = require('express');
const app = express();
const PORT = 4000;

// (ここにサンプルデータを記述)

// REST API エンドポイント
// 全ユーザーを取得
app.get('/users', (req, res) => {
  res.json(users);
});

// IDで特定のユーザーを取得
app.get('/users/:id', (req, res) => {
  const user = users.find(u => u.id === req.params.id);
  if (user) {
    res.json(user);
  } else {
    res.status(404).send('User not found');
  }
});

app.listen(PORT, () => {
  console.log(`REST server is running at http://localhost:${PORT}`);
});

これを実行し、ブラウザやツールで http://localhost:4000/users にアクセスすると、全ユーザーのJSONが返ってきます。

GraphQLの実装

次に、GraphQLをセットアップします。スキーマ(データの設計図)とリゾルバ(データをどう取得するかの処理)を定義するのが特徴です。

const express = require('express');
const { graphqlHTTP } = require('express-graphql');
const { buildSchema } = require('graphql');
const app = express();
const PORT = 4001;

// (ここにサンプルデータを記述)

// GraphQL スキーマ定義
const schema = buildSchema(`
  type User {
    id: ID!
    name: String
    email: String
  }

  type Query {
    users: [User]
    user(id: ID!): User
  }
`);

// リゾルバ
const root = {
  users: () => users,
  user: ({ id }) => users.find(u => u.id === id),
};

// GraphQL エンドポイント
app.use('/graphql', graphqlHTTP({
  schema: schema,
  rootValue: root,
  graphiql: true, // 開発用のUIを有効にする
}));

app.listen(PORT, () => {
  console.log(`GraphQL server is running at http://localhost:${PORT}/graphql`);
});

http://localhost:4001/graphql にアクセスすると、GraphiQLという開発ツールが表示されます。ここで以下のようなクエリを試してみてください。

query {
  users {
    id
    name
  }
}

すると、emailを含まない、指定した通りのデータが返ってくるはずです。これがGraphQLの力です!

ステップ2: 機能の拡張

ユーザーに関連する投稿情報も取得できるようにしてみましょう。

RESTの場合

新しいエンドポイントを追加します。

// 特定のユーザーの投稿を取得
app.get('/users/:id/posts', (req, res) => {
  const userPosts = posts.filter(p => p.userId === req.params.id);
  res.json(userPosts);
});

IDが1のユーザーの情報と、そのユーザーの投稿を取得するには、/users/1/users/1/postsの2回リクエストが必要です。

GraphQLの場合

スキーマとリゾルバを修正するだけです。

// スキーマを修正
const schema = buildSchema(`
  type Post {
    id: ID!
    title: String
  }

  type User {
    id: ID!
    name: String
    email: String
    posts: [Post]
  }

  type Query {
    users: [User]
    user(id: ID!): User
  }
`);

// Userタイプリゾルバにpostsを追加
const root = {
  users: () => users,
  user: ({ id }) => users.find(u => u.id === id),
};

// Userオブジェクトにpostsフィールドが要求された際の処理を追加
// 今回は簡易化のため、スキーマ定義にリゾルバを埋め込むような考え方で進めます。
// 実際にはUser型のリゾルバを明示的に定義します。
const userResolver = (user) => ({
    ...user,
    posts: () => posts.filter(p => p.userId === user.id)
})

// リゾルバの修正
const rootWithPosts = {
    users: () => users.map(userResolver),
    user: ({ id }) => {
        const user = users.find(u => u.id === id);
        return user ? userResolver(user) : null;
    }
};

// app.use の rootValue を rootWithPosts に変更
app.use('/graphql', graphqlHTTP({
  schema: schema,
  rootValue: rootWithPosts, // ここを変更
  graphiql: true,
}));

以下のクエリを実行すると、ユーザー情報と投稿リストが一度に取得できます。

query {
  user(id: "1") {
    name
    posts {
      title
    }
  }
}

ステップ3: 実用的な応用

フロントエンドのJavaScriptからfetchを使ってデータを取得する例です。

// REST: 2回のリクエストが必要
async function fetchUserDataREST(userId) {
  const userRes = await fetch(`http://localhost:4000/users/${userId}`);
  const user = await userRes.json();

  const postsRes = await fetch(`http://localhost:4000/users/${userId}/posts`);
  const posts = await postsRes.json();

  console.log({ ...user, posts });
}

// GraphQL: 1回のリクエストで完了
async function fetchUserDataGraphQL(userId) {
  const query = `
    query GetUserWithPosts($userId: ID!) {
      user(id: $userId) {
        name
        email
        posts {
          title
        }
      }
    }
  `;

  const res = await fetch('http://localhost:4001/graphql', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ 
      query,
      variables: { userId: userId }
    }),
  });

  const data = await res.json();
  console.log(data.data.user);
}

コードの量やリクエスト回数に明確な違いが出ることが分かりますね。

ステップ4: チーム開発を意識した改善

  • REST: APIの仕様書(ドキュメント)を別途OpenAPI (Swagger)などでしっかり管理することが重要です。そうしないと、フロントエンドとバックエンドで認識の齟齬が生まれます。
  • GraphQL: スキーマ自体が「型定義されたドキュメント」として機能します。GraphiQLのようなツールを使えば、常に最新のAPI仕様を確認しながら開発を進められるため、チーム内のコミュニケーションが円滑になります。

実際の開発現場での活用

REST APIでは複数回のリクエストが必要で、GraphQLでは1回で済むことを示すイラスト。データの流れを視覚的に表現。

どちらの技術も素晴らしいものですが、万能薬ではありません。プロジェクトの特性に合わせて使い分ける「適材適所」の視点が重要です。

業務での使用例

  • RESTが適しているケース:

    • シンプルなCRUD(作成、読み取り、更新、削除)操作が中心のアプリケーション。
    • マイクロサービス間の内部通信など、リソースの構造が明確で安定している場合。
    • HTTPキャッシュを最大限に活用したい場合。
  • GraphQLが適しているケース:

    • Web、iOS、Androidなど、多様なクライアントが存在し、それぞれが必要とするデータが異なる場合。
    • 複雑なデータ構造を持ち、関連データを一度に取得したいニーズが強いアプリケーション(例:SNSのフィード画面)。
    • フロントエンドの開発速度を上げたい場合(バックエンドの変更を待たずに、必要なデータをクエリで指定できるため)。

チーム開発でのベストプラクティス

GraphQLの「スキーマ駆動開発」は非常に強力です。まずAPIのスキーマ(仕様)を定義し、それを契約書としてフロントエンドとバックエンドが並行して開発を進めることができます。モックサーバーもスキーマから自動生成できるため、バックエンドの実装を待たずにフロントエンドの開発やテストが可能です。

RESTでも、OpenAPIなどを用いて同様のアプローチ(コントラクトファースト)が可能ですが、GraphQLは言語自体にその仕組みが組み込まれている点が強みです。

保守性を意識した書き方

  • REST: エンドポイントが増え続けると管理が大変になります。バージョニング(例: /v1/users, /v2/users)をどうするかは大きな課題です。一貫した命名規則やレスポンス形式をチームで定めることが不可欠です。
  • GraphQL: スキーマは後からフィールドを追加するのは簡単ですが、削除するのは注意が必要です。古いフィールドをいきなり消すのではなく、@deprecated ディレクティブを使って「非推奨」マークを付け、どのクライアントからも使われなくなったことを確認してから削除する、という段階的な移行が推奨されます。

よくあるつまずきポイントと解決策

学習の過程でつまずくのは当たり前。ここでは、初心者が陥りがちな問題とその乗り越え方を紹介します。

初心者が陥りやすい問題

  • RESTのオーバー/アンダーフェッチ: 「とりあえず全部返す」という実装をしがちですが、本当に必要なデータだけを返すように意識しましょう。アンダーフェッチは、バックエンドに特定のユースケースに特化したエンドポイントを用意することで解決できる場合があります。
  • GraphQLのN+1問題: リゾルバの実装が素朴だと、深刻なパフォーマンス問題を引き起こすことがあります。例えば、10人のユーザーの投稿を取得する際に、ユーザー取得で1回、各ユーザーの投稿取得で10回の計11回データベースにクエリが発行されてしまう問題です。これは「DataLoader」というライブラリを使うことで、効率的に解決できます。

エラーメッセージの読み方

  • REST: エラーは主にHTTPステータスコードで表現されます(例: 400 Bad Request, 401 Unauthorized, 404 Not Found)。レスポンスボディに詳細なエラーメッセージを含めるのが一般的です。
  • GraphQL: 部分的な失敗があり得るため、リクエスト自体は成功(HTTP 200 OK)し、レスポンスボディ内の errors 配列にエラー情報が含まれる、という形式が基本です。最初は戸惑うかもしれませんが、「データは一部取れたけど、この部分はエラーだった」という柔軟な表現ができるのが利点です。

デバッグの基本的な考え方

  • REST: PostmanやInsomnia、あるいはブラウザの開発者ツールやcURLコマンドが強力な味方です。リクエストとレスポンスを直接確認し、問題の切り分けを行います。
  • GraphQL: GraphiQLやApollo Studio Playgroundのような対話的な開発環境が非常に便利です。クエリを少しずつ組み立てながら、リアルタイムに結果を確認できます。エラー箇所も分かりやすく表示してくれます。

継続的な学習のために

基本をマスターしたら、さらに実践的なトピックに挑戦してみましょう。

次に学ぶべきこと

  • 認証と認可: 誰がAPIを使えるのか、どのデータにアクセスできるのかを制御する仕組みです(JWT, OAuthなど)。
  • キャッシュ: よく使われるリクエストの結果を一時的に保存し、パフォーマンスを向上させる技術。
  • パフォーマンスチューニング: 特にGraphQLでは、DataLoaderを使ったN+1問題の解決は必須スキルです。
  • リアルタイム通信: GraphQLの「サブスクリプション」を使えば、チャットアプリのようなリアルタイムなデータ更新を実現できます。

おすすめの学習リソース

特定のサービス名は挙げませんが、一番信頼できるのは、各技術の「公式ドキュメント」です。最初は難しく感じるかもしれませんが、正確で最新の情報が詰まっています。また、実際に動いているオープンソースプロジェクトのコードを読んでみるのも、現場のプラクティスを学ぶ上で非常に有効です。

コミュニティとの関わり方

プログラミング学習は、一人で抱え込まないことが大切です。技術系の勉強会に参加したり、オンラインのフォーラムで質問したりしてみましょう。他の人がどんなことで悩んでいるのかを知るだけでも、視野が広がります。また、自分の書いたコードをGitHubなどで公開し、フィードバックを求めるのも素晴らしい学習方法です。

まとめ:成長のための次のステップ

ここまで、GraphQLとREST APIの基本から実践まで、一緒に旅をしてきました。いかがでしたか?

重要なのは、「どちらが絶対的に優れている」という銀の弾丸は存在しないということです。それぞれの技術には、生まれた背景と得意なことがあります。あなたが今作ろうとしているアプリケーションの特性、チームのスキル、将来の拡張性を考え、最適な道具を選ぶ。それがエンジニアとしての設計能力です。

今日学んだことを、ぜひ自分の手で試してみてください。小さなWebサイトのAPIでも、個人的なツールでも構いません。実際に作ってみることで、知識は本当の意味であなたの血肉となります。失敗を恐れず、たくさんのコードを書き、そして楽しんでください。その一歩一歩が、あなたを素晴らしいエンジニアへと成長させてくれるはずです。

これからも、あなたのプログラミング学習の旅を心から応援しています!

関連記事