フロントエンドとサーバーサイドの連携は、Reactアプリケーションの重要な側面です。効率的かつ型安全にAPIを利用することで、アプリケーションの信頼性と保守性を向上させることができます。本章では、GraphQLとREST APIの使い分け、データフェッチライブラリの活用、型安全性の確保、およびリアルタイム通信の実装について解説します。
1. GraphQLとREST APIの使い分けと統合
REST API
- 特徴:
- シンプルで広く採用されている。
- HTTPメソッド (GET, POST, PUT, DELETE) に基づくリソース指向。
- 利点:
- 設定が簡単で、学習コストが低い。
- キャッシュコントロールが容易。
- 欠点:
- エンドポイントの肥大化 (オーバーフェッチやアンダーフェッチの問題)。
GraphQL
- 特徴:
- クエリ言語を使用して必要なデータのみを取得。
- 1つのエンドポイントで複数のデータソースにアクセス可能。
- 利点:
- 柔軟なデータ取得。
- 型システムを備えたスキーマにより自己文書化が可能。
- 欠点:
- サーバーの設定が複雑。
- クライアントサイドでキャッシュ戦略を慎重に設計する必要がある。
適切な使い分け
- REST API:
- シンプルな操作 (例: ユーザーのCRUD操作)。
- キャッシュを活用したパフォーマンス向上が重要な場合。
- GraphQL:
- 複雑なクエリが必要な場合。
- クライアントが柔軟にデータを取得する必要がある場合。
2. Apollo ClientやReact Queryの活用方法
Apollo Client
- GraphQL専用のデータフェッチライブラリ。
- 主要な機能:
- クエリ、ミューテーション、サブスクリプションのサポート。
- キャッシュ管理。
- 使用例:
tsx
import { ApolloClient, InMemoryCache, ApolloProvider, useQuery, gql } from "@apollo/client";
const client = new ApolloClient({
uri: "https://example.com/graphql",
cache: new InMemoryCache(),
});
const GET_USERS = gql`
query GetUsers {
users {
id
name
}
}
`;
const Users = () => {
const { loading, error, data } = useQuery(GET_USERS);
if (loading) return <p>Loading...</p>;
if (error) return <p>Error: {error.message}</p>;
return (
<ul>
{data.users.map((user: { id: string; name: string }) => (
<li key={user.id}>{user.name}</li>
))}
</ul>
);
};
const App = () => (
<ApolloProvider client={client}>
<Users />
</ApolloProvider>
);
React Query
- REST APIやGraphQLに対応する汎用的なデータフェッチライブラリ。
- 主要な機能:
- データのキャッシュ管理。
- リトライ、再フェッチ、ポーリングのサポート。
- 使用例:
tsx
import { useQuery } from "react-query";
const fetchUsers = async () => {
const response = await fetch("https://example.com/api/users");
if (!response.ok) throw new Error("Network response was not ok");
return response.json();
};
const Users = () => {
const { data, error, isLoading } = useQuery("users", fetchUsers);
if (isLoading) return <p>Loading...</p>;
if (error) return <p>Error: {error.message}</p>;
return (
<ul>
{data.map((user: { id: string; name: string }) => (
<li key={user.id}>{user.name}</li>
))}
</ul>
);
};
3. 型安全なAPI呼び出し (TypeScriptと自動生成された型の活用)
型安全性の重要性
- フロントエンドとバックエンド間のデータ不整合を防止。
- 自動補完による開発体験の向上。
GraphQLコード生成
- ツール: GraphQL Code Generator
- 使用例:
bash
npx graphql-codegen init
yaml
schema: "https://example.com/graphql"
documents: "src/**/*.graphql"
generates:
src/generated/graphql.ts:
plugins:
- "typescript"
- "typescript-operations"
- "typescript-react-apollo"
REST APIの型生成
- ツール: SwaggerやOpenAPIと組み合わせて型を自動生成。
- 使用例:
4. リアルタイム通信 (WebSocketやSSEの実装)
WebSocketの使用例
- 双方向通信が可能なプロトコル。
tsx
import { useEffect, useState } from "react";
const useWebSocket = (url: string) => {
const [messages, setMessages] = useState<string[]>([]);
useEffect(() => {
const socket = new WebSocket(url);
socket.onmessage = (event) => {
setMessages((prev) => [...prev, event.data]);
};
return () => socket.close();
}, [url]);
return messages;
};
const Chat = () => {
const messages = useWebSocket("wss://example.com/chat");
return (
<ul>
{messages.map((msg, index) => (
<li key={index}>{msg}</li>
))}
</ul>
);
};
Server-Sent Events (SSE)
- クライアントからのリクエストなしにサーバーがデータを送信可能。
tsx
import { useEffect, useState } from "react";
const useSSE = (url: string) => {
const [events, setEvents] = useState<string[]>([]);
useEffect(() => {
const eventSource = new EventSource(url);
eventSource.onmessage = (event) => {
setEvents((prev) => [...prev, event.data]);
};
return () => eventSource.close();
}, [url]);
return events;
};
const Notifications = () => {
const events = useSSE("https://example.com/notifications");
return (
<ul>
{events.map((event, index) => (
<li key={index}>{event}</li>
))}
</ul>
);
};
サーバーサイドとの連携は、アプリケーションの動的なデータ処理に不可欠です。本章で紹介した方法を組み合わせて、信頼性とパフォーマンスを兼ね備えたアプリケーションを構築しましょう。