なぜGraphQLはReactと相性がいいのか|RESTとの違いから理解する

はじめに

これまでにReactやGraphQLを学び、実際にアプリを作って記事にもしてきました。

ただ正直に言うと——「動くものは作れたけれど、なぜGraphQLとReactが一緒に語られるのか、その便利さを実感しきれていない」状態でした。

RESTとの違いは何となく知っていても、「実際にReactで開発するときにどんな風に効いてくるのか?」が腹落ちしていなかったのです。

この記事では、アプリをゼロから作らなくてもイメージできるように、文字多め+最小限のコード断片で 「GraphQLとReactの相性の良さ」 を整理していきます。

あわせて、RESTと比べたときの違いも丁寧に触れていきます。

ゴールは“写経”ではなく、“納得”です!


React編:なにをする道具?

Reactは「UIを部品に分けるためのライブラリ」

  • 役割:画面(UI)をコンポーネントという小さな部品に分けて作る
  • 価値:部品ごとに責務がはっきりする → 読みやすい、直しやすい、再利用しやすい
  • 合言葉:部品化・状態・単方向データフロー

最小の書き方イメージ

コンポーネント(見た目とロジックの一まとまり)

function HelloMessage() {
  return <p>こんにちは</p>;
}

解説

  • function HelloMessage() { ... }
    これは「関数コンポーネント」と呼ばれる最も基本的なReactコンポーネントの書き方です。
    関数名は必ず大文字で始める(HelloMessage)というルールがあります。小文字で始めるとただの関数扱いになってしまい、JSX内で呼び出せなくなるためです。
  • return <p>こんにちは</p>;
    関数の戻り値としてJSX(JavaScript XML)を返しています。
    <p>こんにちは</p> は通常のHTMLに似ていますが、JavaScript内に直接書けるのがポイントです。

Reactでは、「コンポーネント=UI部品を返す関数」とまず理解しておくと良いです。

Props(外から渡す値)

function HelloMessage(props) {
  return <p>こんにちは、{props.name}</p>;
}

// 呼び出し側
<HelloMessage name="Taro" />

解説

  • props は「プロパティ」の略で、親コンポーネントから子コンポーネントに渡される値をまとめたオブジェクトです。
  • {props.name} の部分は、JavaScriptの変数展開です。JSX内では {} を使ってJavaScriptの式を埋め込めます。
  • 呼び出し側で <HelloMessage name="Taro" /> と書くと、props の中に { name: "Taro" } が入ります。

Reactは「部品を組み合わせる」ライブラリなので、この Propsの仕組みがUIの再利用性を支える核になります。

State(中で変わる値)とイベント

import { useState } from "react";

function Counter() {
  const [count, setCount] = useState(0);
  return (
    <button onClick={() => setCount(count + 1)}>
      いまの数: {count}
    </button>
  );
}

解説

  • import { useState } from "react";
    Reactのフック(Hook)と呼ばれる機能を読み込んでいます。フックは関数コンポーネントに「状態」や「副作用処理」などの機能を追加する仕組みです。
  • const [count, setCount] = useState(0);
    useState は「状態を作る」フックです。
    • count … 状態の値(初期値は0)
    • setCount … 状態を更新する関数
      という2つのものが配列として返ってくるので、分割代入を使ってそれぞれに名前を付けています。
  • <button onClick={() => setCount(count + 1)}>
    onClick はクリック時に実行されるイベントハンドラ。
    setCount(count + 1) と書くと、count が1増えて再レンダリングされます。

Reactでは「状態が変わればUIが自動で更新される」ので、わざわざdocument.querySelectorinnerTextのようにDOMを触る必要がありません。ここが一番の便利さです。

単方向データフロー(親→子)

function Parent() {
  const [keyword, setKeyword] = useState("");
  return (
    <>
      <SearchBox onChange={setKeyword} />
      <ResultList query={keyword} />
    </>
  );
}

解説

  • 親コンポーネントが keyword という状態を持ち、子コンポーネントに値や関数を渡しています。
  • <SearchBox onChange={setKeyword} />
    • 子から入力イベントがあったときに、親の状態を更新するために setKeyword を渡しています。
  • <ResultList query={keyword} />
    • 親の状態(keywordの値)をそのまま渡しています。

Reactの世界では、データの流れは常に「親→子」です。
子から直接親を操作することはできず、代わりに「親が関数を渡す → 子がそれを呼ぶ」形を取ります。この「単方向データフロー」のルールのおかげで、状態の所在が明確になり、バグの原因が追いやすくなります。

Reactの実感ポイント

  • DOM操作を自分で書かなくていい(状態が変わればUIが勝手に同期)
  • 責務が分かれる(フォームはフォーム、一覧は一覧)
  • 小さく分けて積み上げられる(部品を組み合わせる感覚)

GraphQL編:なにをする道具?

GraphQLは「欲しいデータだけを宣言して取る仕組み」

  • 役割:APIからデータを取るときの“言語”(RESTの代替/補完)
  • 価値:必要なフィールドだけを一度に取得 → 無駄が少なく、往復も減る
  • 合言葉:スキーマ・クエリ・型安全

最小の書き方イメージ

欲しいデータを宣言するクエリ
(ここでは例として、GitHubユーザー検索用のコードを抜粋しています)

query SearchUsers($q: String!) {
  search(query: $q, type: USER, first: 5) {
    nodes {
      ... on User {
        id
        login
        avatarUrl
      }
    }
  }
}

解説

  • query SearchUsers($q: String!) { ... }
    • query は「データを取得する操作」を意味します。
    • SearchUsers はクエリに付けた任意の名前(わかりやすくするためのラベル)。
    • ($q: String!) は「変数の宣言」。
      • $q が変数名。
      • String! は型指定で「文字列が必須」という意味です。
  • search(query: $q, type: USER, first: 5)
    • GitHub GraphQL APIのsearchフィールドを呼び出しています。
    • query: $q で、先ほど宣言した変数 $q を渡しています。
    • type: USER は「ユーザーを探す」という指定。
    • first: 5 は「最初の5件を取得する」という意味です。
  • nodes { ... on User { ... } }
    • nodes の中には検索でヒットした要素が並びます。
    • ... on User は「型条件付きフラグメント」と呼ばれ、User型のデータが来た場合だけ以下を取得するという意味です。
    • { id, login, avatarUrl } で欲しいフィールドを列挙。これだけが返ってきます。

GraphQLは「必要なフィールドだけ宣言する」のが最大の特徴。
「ユーザーの名前だけ欲しい」「画像URLも欲しい」といったニーズに応じてクエリを書き換えれば、その通りのデータが返ってきます。

変数を渡して実行

runQuery({
  query: SearchUsers,
  variables: { q: "torvalds" }
}).then(result => {
  console.log(result.data.search.nodes);
});

解説

  • runQuery({ query: SearchUsers, variables: { q: "torvalds" } })
    • 実際にはApollo ClientやRelayといったライブラリがこの役割を担います。
    • query には先ほど書いたGraphQLクエリを指定。
    • variables に、クエリで宣言した $q の値を与えています。ここでは "torvalds"
  • .then(result => { ... })
    • 実行結果はPromiseとして返ってくるので、.thenで処理します。
    • result.data.search.nodes に、先ほど指定した id, login, avatarUrl が入った配列が格納されます。

RESTだと「/users?q=torvalds」のようにエンドポイントやクエリパラメータを叩きますが、GraphQLは「1つのエンドポイントに対してクエリ文+変数を送る」という考え方です。
これにより、エンドポイントの数が爆発せず、取得データも柔軟に制御できるというメリットがあります。


RESTと比べると?

REST

GET /users?query=torvalds
// サーバーが用意した固定の形で返る

GraphQL

POST /graphql
{
  query: "... id login avatarUrl だけちょうだい ..."
}

GraphQLの実感ポイント

  • 過剰取得が起きにくい
  • 関連データを一発で取れる
  • スキーマで「何が取れるか」が明示される

React × GraphQLはどう噛み合う?

役割分担がとても自然です。

  • React:UIの部品化と状態管理(画面側)
  • GraphQL:必要なデータの宣言と取得(データ側)

擬似コードのイメージ

function UserList({ keyword }) {
  const { data, loading, error } = useQuery(SearchUsers, { variables: { q: keyword } });
  if (loading) return <p>読み込み中...</p>;
  if (error)   return <p>エラー</p>;
  return data.search.nodes.map(u => <UserItem key={u.id} user={u} />);
}

UIとデータ取得の責務がきれいに分かれる。

「UIはどう見せる?」「データは何が必要?」と整理しやすい。


どんなときにメリットが立つか?

  • 小規模(検索して名前だけ表示) → RESTで十分
  • 中規模(名前+アイコン+リポジトリ数など複数の関連データ) → GraphQLが効いてくる
  • 大規模(一覧・詳細・条件検索などUIが複雑) → Reactの部品化+GraphQLの型安全性が効く

よくあるつまずきと抜け方

  1. 「動いたけど実感がない」
    → わざと「関連データをまとめて取りたい」状況を作るとGraphQLが光る
  2. 「用語が多くて混乱」
    → Reactは「部品・状態・親→子」、GraphQLは「スキーマ・クエリ・型」だけ覚えればOK
  3. 「コード全部を覚えようとして疲れる」
    → どの責務の話(UI or データ)かを切り分けて見る

まとめ

ReactとGraphQLを一緒に触ってみると、最初は「別々のもの」に見えるかもしれません。
でも実際に手を動かしてみると、だんだんこう感じるはずです。

  • React=「UIの部品担当」。状態が変われば、自然と画面が変わってくれる。
  • GraphQL=「データ取得担当」。欲しい形だけを、一度でピンポイントに持ってきてくれる。

この2つは役割が重ならず、まるで歯車のように噛み合います。UIを小さく分ければ分けるほど、「この部品に必要なデータ」が明確になる。

するとGraphQLのクエリもスッキリ短くなり、データの流れが頭の中でカチッと整理される。

僕自身も、ここまで記事を書いてやっと「あ、そういうことか」と腹落ちしてきました。

ただコードを追うだけではなく、「役割の違いと噛み合い」を意識することで、ReactとGraphQLの本当の便利さがじわっと実感できるようになると思います。

次のステップ

React、GraphQLの良さが理解できたところで、デモアプリを作って理解を深めてきましょう!

もっと徹底的に基本を理解したい方は、以下記事もおススメです!