📝

axiosの共通関数を作成してみた

2023/09/23に公開

以下のbulletproof-reactのコードを見ていたら、axiosの共通関数を見つけ、疑問に思うこともあったので自分で作成しながら調べてみました。
https://github.com/alan2207/bulletproof-react/

実際に作成したコードはこちらです。

import Axios, { InternalAxiosRequestConfig, AxiosResponse } from "axios";

/** ラッピングされたaxios関数 */
export const axios = Axios.create({
  baseURL: API_URL, // ベースURLの設定
  timeout: 5000, // タイムアウトの設定
});

// 以下が任意の共通設定
// リクエスト時の共通設定
axios.interceptors.request.use(authRequestInterceptor);
// レスポンス時の共通設定
axios.interceptors.response.use(
  // 2xx の範囲内にあるステータスコードはこの関数が実行される
  (response: AxiosResponse<SuccessResponse<unknown>>) => {
    // ここで成功時の共通設定
    return response;
  },
  // 2xx の範囲外のステータスコードはこの関数が実行される
  (error: ErrorResponse) => {
    // ここで失敗時の共通設定
    return Promise.reject(error)
  }
)

axiosの新しいインスタンスを作成し、interceptorを使用してリクエストとレスポンス処理がされる前に共通の設定を実装していきました。

リクエスト時

リクエスト時の共通設定はauthRequestInterceptorにまとめましたが、内容は以下です。

/**
 * 各リクエストごとにヘッダーを設定するための関数
 * 
 * @param config
 * @return 設定したヘッダー
 */
function authRequestInterceptor(config: InternalAxiosRequestConfig) {
  // アクセストークンの設定
  const token = getToken();

  if (token) {
    config.headers.Authorization = token;
  }

  config.headers.Accept = "application/json";
  return config;
}

アクセストークンを取得して、取得できた場合はheadersのAuthorizationにアクセストークンを設定しました。アクセストークンの有無に関わらず設定したのがAcceptで"application/json"を指定しています。
現在のプロジェクトでは都度トークンを設定することとAcceptやContent-typeを設定しているのですが、こうすることによって、都度headers情報を設定する必要がなくなりました。

レスポンス時

レスポンス時に関してもinterceptorを使用すれば、第一引数にステータスコードが2xx範囲内の時に実行したいことを、第二引数にステータスコードが2xx範囲外の時に実行したいことを実装すれば共通設定ができるようになります。

// レスポンス時の共通設定
axios.interceptors.response.use(
  // 2xx の範囲内にあるステータスコードはこの関数が実行される
  (response: AxiosResponse<SuccessResponse<unknown>>) => {
    // ここで成功時の共通設定
    return response;
  },
  // 2xx の範囲外のステータスコードはこの関数が実行される
  (error: ErrorResponse) => {
    // ここで失敗時の共通設定
    return Promise.reject(error)
  }
)

疑問に思ったこと

私はbulletproof-reactのコードを参考(というよりほぼ一緒です)に作成したのですが、疑問に思っていたことはリクエスト時のheadersのContent-typeの指定をどう実装するのかです。
現在のプロジェクトでは都度設定しているとお伝えしましたが、Content-typeも指定しており、「application/json」であったり、FormDataの場合は「multipart/form-data」を指定しているので、実際のリクエスト時のデータを確認しなければ、指定できないなと思っていました。
どう実装しようかいろいろ調べていたところ、axiosではリクエスト時に渡したデータによって自動的にContent-typeを指定してくれることがわかりました。

実際に作成した上記のコードに対して以下のデータを渡すと、Content-typeは「application/json」が指定されています。

  const params = {
    test: 1,
  }

次はFormDataを渡すと、Content-typeは「multipart/form-data」が指定されています。

  const form = new FormData();
  form.append('test', "1")

このように自動的にContent-typeが指定されるため、私が悩んでいたContent-typeの指定は必要ありませんでした。

注意点

自動的にContent-typeは指定されますが、仮にコード内でContent-typeを指定するとデータの形関係なく指定したContent-typeになります。

共通関数内でContent-typeの指定をします。

/**
 * 各リクエストごとにヘッダーを設定するための関数
 * 
 * @param config
 * @return 設定したヘッダー
 */
function authRequestInterceptor(config: InternalAxiosRequestConfig) {
  // アクセストークンの設定
  const token = getToken();

  if (token) {
    config.headers.Authorization = token;
  }
  
  config.headers.Accept = "application/json";
  config.headers["Content-Type"] = "application/json";
  return config;
}

このコードに対してFormDataを渡してもContent-typeは「application/json」であり、リクエストデータもFormDataではないことが確認できました。

  const form = new FormData();
  form.append('test', "1")


最後に

今回はbulletproof-reactのコードを参考にaxiosの共通関数を作成しました。
他にもreact-queryもあったので、参考にしながら理解できないこと、疑問に思ったことは調べながら共通関数を作成して個人開発で使っていこうと思います。

Discussion