apollo-client、graphql-request、react-apollo、urql は GraphQL クエリの実行を助けますが、キャッシュ管理や React との統合方法が大きく異なります。apollo-client は包括的なキャッシュ機能を提供し、urql は軽量で拡張性が高いです。graphql-request は最小限の機能でサーバーサイドに適しています。react-apollo は非推奨であり、新しいプロジェクトでは使用すべきではありません。
現代の React アプリケーションにおいて、GraphQL クライアントの選定はアーキテクチャ全体に影響を与えます。apollo-client、urql、graphql-request はそれぞれ異なる哲学を持って設計されており、react-apollo は歴史的な経緯から特別な注意が必要です。ここでは、実務的な観点からこれらを比較します。
まず最初に明確にすべき点があります。react-apollo は現在、公式に非推奨(deprecated)となっています。
react-apollo
@apollo/client に統合されています。// react-apollo: 非推奨(レガシーコード)
import { Query } from 'react-apollo';
// 古いコンポーネントベースの API
<Query query={GET_USER}>
{({ loading, data }) => loading ? <Spinner /> : <User data={data} />}
</Query>
apollo-client (現代の @apollo/client)
// apollo-client: 現在の標準
import { useQuery } from '@apollo/client';
function User() {
const { loading, data } = useQuery(GET_USER);
return loading ? <Spinner /> : <User data={data} />;
}
データキャッシュの有无は、クライアント選定における最大の分岐点です。
apollo-client
// apollo-client: 自動キャッシュ更新
import { useQuery, useMutation } from '@apollo/client';
function UserProfile() {
const { data } = useQuery(GET_USER);
const [updateName] = useMutation(UPDATE_USER);
// ミュテーション実行後、キャッシュが自動的に更新され UI が反映される
}
urql
// urql: シンプルなキャッシュ
import { useQuery } from 'urql';
function UserProfile() {
const [result] = useQuery({ query: GET_USER });
// デフォルトでキャッシュされるが、Apollo より設定が簡素
}
graphql-request
// graphql-request: キャッシュなし
import { request } from 'graphql-request';
async function getUser() {
// 毎回ネットワークリクエストが発生する
const { user } = await request(endpoint, GET_USER);
return user;
}
React コンポーネント内でどのようにデータを扱うかも重要な違いです。
apollo-client
ApolloProvider でアプリを囲む必要があります。useQuery、useMutation などの専用フックが充実しています。// apollo-client: Provider 設定
import { ApolloProvider, ApolloClient, InMemoryCache } from '@apollo/client';
const client = new ApolloClient({ uri: '/graphql', cache: new InMemoryCache() });
function App() {
return <ApolloProvider client={client}><Dashboard /></ApolloProvider>;
}
urql
Provider でアプリを囲みますが、設定がより簡潔です。useQuery は配列 destructuring を使用します。// urql: Provider 設定
import { Provider, createClient } from 'urql';
const client = createClient({ url: '/graphql' });
function App() {
return <Provider value={client}><Dashboard /></Provider>;
}
graphql-request
useEffect などで手動管理が必要です。// graphql-request: React での手動管理
import { request } from 'graphql-request';
import { useEffect, useState } from 'react';
function Dashboard() {
const [data, setData] = useState(null);
useEffect(() => {
request(endpoint, GET_USER).then(setData);
}, []);
// 状態管理を自分で行う必要がある
}
リクエストの前後に処理を挟む機能(ミドルウェア)の比較です。
apollo-client
Link システムを使用します。// apollo-client: Link による拡張
import { setContext } from '@apollo/client/link/context';
const authLink = setContext((_, { headers }) => ({
headers: { ...headers, authorization: token }
}));
// client 作成時に link を結合する
urql
Exchange システムを使用します。// urql: Exchange による拡張
import { fetchExchange, cacheExchange } from '@urql/core';
const client = createClient({
url: '/graphql',
exchanges: [cacheExchange, fetchExchange]
// ここにカスタム exchange を挟み込む
});
graphql-request
// graphql-request: 手動ラッパー
async function requestWithAuth(query, variables) {
const headers = { authorization: token };
return request(endpoint, query, variables, headers);
}
| 特徴 | apollo-client | urql | graphql-request | react-apollo |
|---|---|---|---|---|
| キャッシュ | ✅ 正規化キャッシュ | ✅ シンプルキャッシュ | ❌ なし | ✅ 旧キャッシュ |
| React 統合 | ✅ 専用フック | ✅ 専用フック | ❌ 手動管理 | ✅ 旧コンポーネント |
| 拡張性 | ✅ Link システム | ✅ Exchange システム | ❌ 手動ラッパー | ✅ 旧 Link |
| 状態 | ✅ 維持中 | ✅ 維持中 | ✅ 維持中 | ❌ 非推奨 |
| 用途 | 大規模アプリ | 軽量〜中規模 | スクリプト/単純取得 | 使用禁止 |
apollo-client は、大規模なアプリケーションでデータの一貫性を保ちたい場合に最適です — 学習コストは高いですが、その分の機能があります。
urql は、Apollo の機能は欲しいが重すぎる場合に選ばれます — 設定がシンプルで、カスタマイズも容易です。
graphql-request は、React の状態管理を介さず、単純にデータを取得したい場合に有効です — サーバーサイド処理やバッチスクリプトに向いています。
react-apollo は、技術的な負債となります — 新しいプロジェクトでは絶対に避け、既存プロジェクトからは移行してください。
最終的には、プロジェクトの規模とキャッシュの必要性で決まります — 複雑な状態管理が必要なら Apollo、シンプルさ優先なら urql、単なる取得なら graphql-request です。
キャッシュ不要で単純なデータ取得が必要な場合や、サーバーサイド Node.js スクリプトでの利用に適しています。バンドルサイズを最小限に抑えたい場合にも有効です。
軽量さを保ちつつカスタマイズ性を求めたい場合に最適です。Apollo よりもシンプルな API を好みながら、拡張機能も必要なチームに向いています。
大規模なキャッシュ管理や複雑な状態管理が必要なエンタープライズ向けプロジェクトに適しています。正規化されたキャッシュにより、UI の更新を自動的に処理できるため、データ整合性が重要な場合に選択します。
非推奨パッケージです。新しいプロジェクトでは絶対に使用しないでください。既存プロジェクトからは @apollo/client へ移行すべきです。
Minimal GraphQL client supporting Node and browsers for scripts or simple apps.
async / await)TypedDocumentNodenpm add graphql-request graphql
This package uses package.exports. Therefore if you are a TypeScript user you must:
tsconfig.json moduleResolution set to "bundler" or "node16"/"nodenext".package.json type set to "module".Send a GraphQL document using a static request function:
import { gql, request } from 'graphql-request'
const document = gql`
{
company {
ceo
}
}
`
await request('https://api.spacex.land/graphql/', document)
The function can be passed a configuration object for more complex cases:
await request({
url,
document,
variables,
requestHeaders,
})
A class is available for constructing your own instances:
import { gql, GraphQLClient } from 'graphql-request'
const document = gql`
{
company {
ceo
}
}
`
const endpoint = 'https://api.spacex.land/graphql/'
const client = new GraphQLClient(endpoint)
await client.request(document)
We only (officially) support versions of Nodejs of the following status:
So for example on Oct 24 2023 that would mean these versions: 18, 20, 21.
Any issue that exists solely for an unsupported version of Nodejs will be rejected (not worked on).
⚠️ This reference is incomplete. Check out the examples for more reference material.
By default GraphQLClient will throw when an error is received. However, sometimes you still want to resolve the (partial) data you received.
You can define errorPolicy in the GraphQLClient constructor.
const client = new GraphQLClient(endpoint, { errorPolicy: 'all' })
Allow no errors at all. If you receive a GraphQL error the client will throw.
Ignore incoming errors and resolve like no errors occurred
Return both the errors and data, only works with rawRequest.
OperationName has been introduced to address issues reported here Support operation name, However, on certain occasions this information may not be needed in requests. In such cases, you might consider ignoring operationName to avoid the extraction steps currently performed by a parsing operation when the document is provided in string format.
By default the GraphQLClient tries to extract the operationName from the document.
You can define excludeOperationName in the constructor of GraphQLClient to avoid the extraction process if it is not needed. This can be useful if you don't use operationName and want to optimise queries by reducing the amount of computation as much as possible, especially if we are in a context where we are using documents in string format to reduce bundle size.
// example where the operation name is not ignored
const client = new GraphQLClient(endpoint, {
method: 'POST',
})
// example in which the operation name is ignored
const client = new GraphQLClient(endpoint, {
method: 'POST',
excludeOperationName: true,
})
In this issue we decided to make this library more stable and maintainable. In principal the feature is still in scope of this library and will make a return when we find time to do the feature right.
graphql?graphql-request uses methods exposed by the graphql package to handle some internal logic. On top of that, for TypeScript users, some types are used from the graphql package to provide better typings.
gql template exported by graphql-request?No. It is there for convenience so that you can get the tooling support like automatic formatting and syntax highlighting. You can use gql from graphql-tag if you need it for some reason too.
graphql-request apart from other clients like Apollo, Relay, etc.?graphql-request is the most minimal and simplest to use GraphQL client. It's perfect for small scripts or simple apps.
Compared to GraphQL clients like Apollo or Relay, graphql-request doesn't have a built-in cache and has no integrations for frontend frameworks. The goal is to keep the package and API as minimal as possible.