APIとの通信【fetch, Axios, SWR, React Query】

Webアプリケーション開発において、APIとの通信は必須のスキルとなっています。フロントエンドからバックエンドへのデータの取得や送信を効率的に行うために、様々なライブラリやツールが存在します。この記事では、最も一般的に使用されている4つの通信方法(fetch, Axios, SWR, React Query)について詳しく解説し、それぞれの特徴や使い分けについて紹介します。

目次

  1. JavaScriptネイティブのfetch API
  2. より使いやすいAxios
  3. データ取得のためのSWR
  4. 強力なデータ管理ツールReact Query
  5. それぞれの使い分け
  6. まとめ

JavaScriptネイティブのfetch API

基本概念

fetch APIは、モダンブラウザに組み込まれている標準のAPIで、HTTPリクエストを行うための基本的な機能を提供します。追加のライブラリをインストールする必要がないため、小規模なプロジェクトやシンプルなリクエストに適しています。

基本的な使い方

// GETリクエストの例
fetch('https://api.example.com/data')
  .then(response => {
    // レスポンスが成功(200-299)でない場合はエラーをスロー
    if (!response.ok) {
      throw new Error('Network response was not ok: ' + response.status);
    }
    return response.json(); // JSONとしてパース
  })
  .then(data => {
    console.log('Success:', data);
  })
  .catch(error => {
    console.error('Error:', error);
  });

// POSTリクエストの例
fetch('https://api.example.com/data', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    title: 'New Post',
    content: 'This is the content'
  })
})
  .then(response => response.json())
  .then(data => console.log('Success:', data))
  .catch(error => console.error('Error:', error));

fetchの特徴

メリット

  • 追加のライブラリが不要(モダンブラウザに標準搭載)
  • Promiseベースで非同期処理が書きやすい
  • シンプルなAPI設計

デメリット

  • エラーハンドリングが少し複雑(ネットワークエラーのみをキャッチし、HTTP 400/500エラーはデフォルトでは拒否されない)
  • JSONのパースを手動で行う必要がある
  • リクエストのキャンセル機能が実装されていない(AbortControllerを使用する必要がある)
  • IEではサポートされていない(ポリフィルが必要)

より使いやすいAxios

基本概念

Axiosは、ブラウザとNode.jsの両方で動作する人気のあるHTTPクライアントライブラリです。fetchよりも機能が豊富で、使いやすいAPIを提供します。

インストール

npm install axios
# または
yarn add axios

基本的な使い方

import axios from 'axios';

// GETリクエスト
axios.get('https://api.example.com/data')
  .then(response => {
    console.log('Success:', response.data);
  })
  .catch(error => {
    console.error('Error:', error);
  });

// POSTリクエスト
axios.post('https://api.example.com/data', {
  title: 'New Post',
  content: 'This is the content'
})
  .then(response => {
    console.log('Success:', response.data);
  })
  .catch(error => {
    console.error('Error:', error);
  });

// async/awaitを使用した例
async function fetchData() {
  try {
    const response = await axios.get('https://api.example.com/data');
    console.log('Success:', response.data);
  } catch (error) {
    console.error('Error:', error);
  }
}

高度な使い方

// Axiosインスタンスの作成(共通設定)
const api = axios.create({
  baseURL: 'https://api.example.com',
  timeout: 5000,
  headers: {
    'Authorization': `Bearer ${token}`,
    'Content-Type': 'application/json'
  }
});

// インターセプターの使用
api.interceptors.request.use(config => {
  // リクエスト送信前の処理
  console.log('Request sent:', config);
  return config;
}, error => {
  return Promise.reject(error);
});

api.interceptors.response.use(response => {
  // レスポンス受信後の処理
  console.log('Response received:', response);
  return response;
}, error => {
  // エラーハンドリング
  if (error.response && error.response.status === 401) {
    // 認証エラー時の処理
  }
  return Promise.reject(error);
});

// 複数のリクエストを並行して行う
axios.all([
  api.get('/users'),
  api.get('/posts')
])
  .then(axios.spread((usersResponse, postsResponse) => {
    console.log('Users:', usersResponse.data);
    console.log('Posts:', postsResponse.data);
  }))
  .catch(error => {
    console.error('Error:', error);
  });

Axiosの特徴

メリット

  • JSON変換を自動的に行う
  • リクエストとレスポンスのインターセプター機能
  • リクエストのキャンセル機能
  • タイムアウト設定
  • クロスサイトリクエストフォージェリ(CSRF)保護
  • ブラウザとNode.jsの両方で使用可能
  • 400/500エラーレスポンスを自動的にエラーとして処理

デメリット

  • 追加のライブラリとしてインストールが必要
  • 小規模なプロジェクトでは過剰な場合もある

データ取得のためのSWR

基本概念

SWR(Stale-While-Revalidate)は、Vercelが開発したReactフックベースのデータフェッチライブラリです。「古いデータを表示しながら再検証する」という戦略を採用しており、キャッシュからデータを素早く表示しつつ、バックグラウンドで最新データを取得することで、UXを向上させます。

インストール

npm install swr
# または
yarn add swr

基本的な使い方

import useSWR from 'swr';

// フェッチャー関数の定義
const fetcher = url => fetch(url).then(res => res.json());

function UserProfile({ userId }) {
  const { data, error, isLoading } = useSWR(
    `/api/users/${userId}`,
    fetcher
  );

  if (error) return <div>エラーが発生しました</div>;
  if (isLoading) return <div>ロード中...</div>;
  
  return (
    <div>
      <h1>{data.name}</h1>
      <p>Email: {data.email}</p>
    </div>
  );
}

Axiosと組み合わせる

import useSWR from 'swr';
import axios from 'axios';

const fetcher = url => axios.get(url).then(res => res.data);

function Posts() {
  const { data, error, isLoading } = useSWR('/api/posts', fetcher);
  
  // 以下同様...
}

ミューテーション(データの更新)

import useSWR, { mutate } from 'swr';
import axios from 'axios';

function TodoList() {
  const { data: todos } = useSWR('/api/todos', fetcher);
  
  async function markAsCompleted(id) {
    // 楽観的UI更新
    mutate(
      '/api/todos',
      todos.map(todo => 
        todo.id === id ? { ...todo, completed: true } : todo
      ),
      false // 再検証しない
    );
    
    // APIリクエスト
    await axios.patch(`/api/todos/${id}`, { completed: true });
    
    // キャッシュデータを再検証
    mutate('/api/todos');
  }
  
  return (
    <ul>
      {todos?.map(todo => (
        <li key={todo.id}>
          {todo.title}
          {!todo.completed && (
            <button onClick={() => markAsCompleted(todo.id)}>
              完了
            </button>
          )}
        </li>
      ))}
    </ul>
  );
}

SWRの特徴

メリット

  • 自動再検証(フォーカス時、ネットワーク再接続時など)
  • デデュプリケーション(重複リクエストの排除)
  • キャッシュの管理
  • ページネーション、無限スクロールのサポート
  • タイプセーフ(TypeScriptサポート)
  • 楽観的UI更新
  • 比較的小さなパッケージサイズ

デメリット

  • 主にGETリクエストに特化している
  • 複雑なキャッシュ無効化シナリオでは扱いにくい場合がある
  • React専用(他のフレームワークには対応していない)

強力なデータ管理ツールReact Query

基本概念

React Query(TanStack Query)は、サーバーの状態管理に特化したライブラリで、データのフェッチング、キャッシング、同期、更新を効率的に行うことができます。SWRと同様のメリットを持ちつつ、より多くの機能と柔軟性を提供します。

インストール

npm install @tanstack/react-query
# または
yarn add @tanstack/react-query

セットアップ

import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { ReactQueryDevtools } from '@tanstack/react-query-devtools';

// クライアントの作成
const queryClient = new QueryClient({
  defaultOptions: {
    queries: {
      staleTime: 5 * 60 * 1000, // 5分間はデータを古いと見なさない
      cacheTime: 30 * 60 * 1000, // 30分間キャッシュを保持
      retry: 1, // エラー時に1回再試行
    },
  },
});

function App() {
  return (
    <QueryClientProvider client={queryClient}>
      {/* アプリケーションのコンポーネント */}
      <YourApp />
      
      {/* 開発ツール(開発環境のみ) */}
      <ReactQueryDevtools initialIsOpen={false} />
    </QueryClientProvider>
  );
}

基本的な使い方(クエリ)

import { useQuery } from '@tanstack/react-query';
import axios from 'axios';

function Users() {
  const { data, isLoading, error } = useQuery({
    queryKey: ['users'],
    queryFn: async () => {
      const { data } = await axios.get('/api/users');
      return data;
    },
  });

  if (isLoading) return <div>ロード中...</div>;
  if (error) return <div>エラー: {error.message}</div>;

  return (
    <ul>
      {data.map(user => (
        <li key={user.id}>{user.name}</li>
      ))}
    </ul>
  );
}

ミューテーション(データの更新)

import { useMutation, useQueryClient } from '@tanstack/react-query';
import axios from 'axios';

function AddUserForm() {
  const queryClient = useQueryClient();
  
  const mutation = useMutation({
    mutationFn: newUser => {
      return axios.post('/api/users', newUser);
    },
    onSuccess: () => {
      // ユーザーが追加されたら、usersクエリを無効化して再フェッチを強制
      queryClient.invalidateQueries({ queryKey: ['users'] });
    },
  });

  const handleSubmit = event => {
    event.preventDefault();
    const formData = new FormData(event.target);
    const newUser = {
      name: formData.get('name'),
      email: formData.get('email'),
    };
    
    mutation.mutate(newUser);
  };

  return (
    <form onSubmit={handleSubmit}>
      <input name="name" placeholder="名前" required />
      <input name="email" placeholder="メール" required />
      <button type="submit" disabled={mutation.isPending}>
        {mutation.isPending ? '送信中...' : 'ユーザーを追加'}
      </button>
      {mutation.isError && <p>エラー: {mutation.error.message}</p>}
      {mutation.isSuccess && <p>ユーザーが正常に追加されました!</p>}
    </form>
  );
}

依存クエリ

function UserPosts({ userId }) {
  // ユーザー情報を取得
  const { data: user, isLoading: isLoadingUser } = useQuery({
    queryKey: ['user', userId],
    queryFn: () => axios.get(`/api/users/${userId}`).then(res => res.data),
  });

  // ユーザーが取得できたら、そのユーザーの投稿を取得
  const { data: posts, isLoading: isLoadingPosts } = useQuery({
    queryKey: ['posts', userId],
    queryFn: () => axios.get(`/api/users/${userId}/posts`).then(res => res.data),
    // ユーザーが取得できるまで実行しない
    enabled: !!user,
  });

  if (isLoadingUser) return <div>ユーザーの読み込み中...</div>;
  if (isLoadingPosts) return <div>投稿の読み込み中...</div>;

  return (
    <div>
      <h1>{user.name}の投稿</h1>
      <ul>
        {posts.map(post => (
          <li key={post.id}>{post.title}</li>
        ))}
      </ul>
    </div>
  );
}

React Queryの特徴

メリット

  • 強力なキャッシュ管理
  • 宣言的なクエリとミューテーション
  • ページネーション、無限スクロール、懸念事項の分離
  • デバッグ用の開発ツール
  • サーバーサイドレンダリング(SSR)のサポート
  • 楽観的アップデート
  • 自動再試行と失敗したクエリの回復
  • 依存クエリ(連鎖的なクエリ)
  • クエリのキャンセル

デメリット

  • 学習曲線がやや高い
  • 小規模なプロジェクトでは機能が過剰な場合がある
  • バンドルサイズが大きい(ただし、必要な機能のみを選択的にインポート可能)

それぞれの使い分け

fetchを選ぶケース

  • 追加ライブラリを最小限に抑えたい場合
  • シンプルな単一リクエストのみ必要な場合
  • 小規模なプロジェクトやプロトタイプ
  • ポリフィルを使用してIE対応を行える場合

Axiosを選ぶケース

  • より便利なAPIと高度な機能が必要な場合
  • インターセプターやリクエストキャンセルなどの機能が必要な場合
  • ブラウザとNode.jsの両方で一貫したAPIを使用したい場合
  • XMLHttpRequestベースのソリューションが必要な場合(古いブラウザのサポート)

SWRを選ぶケース

  • Reactアプリケーションで主にデータ取得(GET)に焦点を当てている場合
  • リアルタイムデータやユーザーエクスペリエンスを重視する場合
  • 自動再検証、キャッシュ管理、ステータスの追跡などが必要な場合
  • 小〜中規模のプロジェクトで、軽量なソリューションを求める場合

React Queryを選ぶケース

  • 複雑なデータ操作と状態管理が必要な大規模なReactアプリケーション
  • GET以外にも多くのミューテーション(POST、PUT、DELETEなど)を扱う場合
  • 高度なキャッシュ制御や複雑なフェッチング戦略が必要な場合
  • クエリの依存関係を管理する必要がある場合
  • デバッグツールとエコシステムの充実度を重視する場合

まとめ

4つのAPIとの通信方法を比較してきましたが、それぞれに強みと弱みがあります。プロジェクトの規模や要件に応じて適切なツールを選択することが重要です。

  • fetch: シンプルで標準的、小規模プロジェクトに最適
  • Axios: 使いやすく機能が豊富、多くのユースケースに対応
  • SWR: Reactでのデータ取得に特化した軽量ライブラリ
  • React Query: 複雑なデータ管理と状態を必要とする大規模プロジェクト向け

実際のプロジェクトでは、これらを組み合わせて使用することも珍しくありません。例えば、React QueryやSWRのフェッチャー関数としてAxiosを使用するなど、それぞれの長所を活かした構成も検討してみてください。

最終的には、チームの経験、プロジェクトの要件、パフォーマンス考慮事項などを総合的に判断して、最適なソリューションを選択することをお勧めします。

コメント

タイトルとURLをコピーしました