0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Axiosインターセプター入門:トークン管理とエラーハンドリングを効率化する

Posted at

はじめに

フロントエンド開発では、APIとの連携がほぼ必須です。しかし、「リクエストごとに認証トークンを手動で付与する」「エラーが発生するたびにtry-catchを書く」といった定型的な処理は、面倒でバグの原因になりがちです。

本記事では、Axiosインターセプターを使って、これらの処理を自動化し、コードを簡潔にする方法を解説します。

Axiosのインターセプターとはどういう機能か

Axiosのインターセプターは、HTTPリクエスト・レスポンスの送受信を横取り(インターセプト)し、共通の前処理・後処理を実行する仕組みです。
これにより、すべてのリクエストに対して、同じロジックを一度だけ書けば済むようになります。

  • リクエストインターセプター: リクエストがサーバーに送られる前に実行されます。
    • 認証トークンをヘッダーに追加する。
    • リクエストのデータを加工する。
  • レスポンスインターセプター: レスポンスがコンポーネントに届く前に実行されます。
    • 401 Unauthorizedエラーを検知し、自動でトークンを再発行する
    • APIから返されたエラーメッセージを統一的に処理する。

Axiosのインターセプターを実装してみる

Axiosクライアントのインスタンスを作成する

まず、共通の設定を持つAxiosクライアントのインスタンスを作成します。これにより、アプリケーション全体で同じ設定を共有できます。

// src/api/axiosClient.ts
import axios, { AxiosInstance } from 'axios';

// APIのベースURLを環境変数から取得
const BASE_URL = import.meta.env.VITE_API_BASE_URL;

// Axiosのインスタンスを作成
const axiosClient: AxiosInstance = axios.create({
  baseURL: BASE_URL,
  headers: {
    'Content-Type': 'application/json',
  },
  timeout: 10000, // 10秒でタイムアウト
});

export default axiosClient;

リクエストインターセプターで認証トークンを自動付与する

次に、APIリクエストを送信する際に、認証トークンをAuthorizationヘッダーに自動で付与するインターセプターを設定します。これにより、トークンを毎回手動で設定する必要がなくなります。

// src/api/axiosClient.ts (追記)
import { useAppStore } from '../stores';

axiosClient.interceptors.request.use(
  (config) => {
    // Zustandストアからトークンを取得
    const { userToken } = useAppStore.getState();

    // トークンが存在する場合、Authorizationヘッダーに追加
    if (userToken) {
      config.headers['Authorization'] = `Bearer ${userToken}`;
    }

    return config;
  },
  (error) => {
    return Promise.reject(error);
  }
);

レスポンスインターセプターで認証エラーをハンドリングする

レスポンスインターセプターは、APIから401 Unauthorizedエラーが返ってきた場合に、ログイン画面にリダイレクトするロジックを実装することができます。
※ 実際の運用では、401エラー時にリフレッシュトークンを使ってアクセストークンを再発行し、再試行する実装も一般的です。
本記事では簡略化のため省略しています。

// src/api/axiosClient.ts (追記)
import { useAppStore } from '../stores';

axiosClient.interceptors.response.use(
  (response) => response,
  async (error) => {
    
    // 401認証エラーの場合
    if (error.response?.status === 401) {

        // ログアウト処理
        useAppStore.getState().logout();
        window.location.href = '/login';
    }
    return Promise.reject(error);
  }
);

APIの利用例

インターセプターを設定すれば、コンポーネントはAPI通信の複雑なロジックを意識する必要がなくなります。

import axiosClient from '../api/axiosClient';

const fetchUserProfile = async () => {
  try {
    // 認証ヘッダーを意識することなくAPIを呼び出せる
    const response = await axiosClient.get('/profile');
    console.log('ユーザー情報:', response.data);
  } catch (error) {
    // APIエラーを統一的にハンドリングできる
    console.error('API呼び出しエラー:', error.message);
  }
};

補足:認証不要のAPIではクライアントを分ける

今回の記事では、認証トークンが必要なAPIへの対応にフォーカスしましたが、ログインやパスワードリセットのように認証前でも利用できるAPIがあるケースも多いと思います。

その場合、Axiosクライアントを「認証付き(例: authClient)」と「認証不要(例: publicClient)」で分けて管理するのが一般的です。
用途ごとに命名・設計を明確にしておくことで、以下のメリットがあります。

  • 認証不要APIに誤ってトークンを送ってしまうことを防げる
  • インターセプターの設定をクライアントごとに独立管理できる
  • テストやモックが簡単になる

認証なしのAPIクライアント

// src/api/publicClient.ts
import axios from 'axios';

const publicClient = axios.create({
  baseURL: import.meta.env.VITE_API_BASE_URL,
  headers: {
    'Content-Type': 'application/json',
  },
  timeout: 10000,
});

export default publicClient;

認証なしのAPI利用例

import publicClient from '../api/publicClient';

export const login = async (credentials) => {
  const response = await publicClient.post('/auth/login', credentials);
  return response.data;
};

認証ありのAPI利用例

import authClient from '../api/authClient';

export const getProfile = async () => {
  const response = await authClient.get('/profile');
  return response.data;
};

おわりに

Axiosインターセプターを使うことで、認証トークンの管理やエラーハンドリングといった定型的な処理を自動化し、API通信のロジックをコンポーネントから完全に分離できます。
次回以降の記事では、本記事をベースに、MSWを使ったAxiosのモック方法、loaderを使ったデータ取得やZustandでの状態管理など、より実践的な開発手法を解説していきます。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?