何気なく使っていたaxiosを初心者向けにまとめてみる(2) ~インスタンス化して効率化~

前回[axios](https://github.com/axios/axios)について基礎的な部分をまとめている、
[何気なく使っていたaxiosを初心者向けにまとめてみる](https://qiita.com/natuuu0831/items/a592c0c9cd0a673c2161)の続きになります。

今回はaxiosの応用を。


## インスタンスを作成
axiosの新しいインスタンスを作成することができます。
下記は通常の```axios.get('http://localhost:3333/api/users')```と同じ動作をします。

```js
const instance = axios.create()
instance.get('http://localhost:3333/api/users')
```

また、引数を追加し設定をカスタムしてインスタンス化をすることができます。
これにより、共通化したい部分をまとめることができます。

```js
const instance = axios.create({
  baseURL: 'http://localhost:3333',
  headers: {
    'Content-Type': 'application/json',
  },
  timeout: 2000,
})

instance.get('/api/users')

instance.post('/api/users', {name: 'test'})
```
上記により、インスタンス化された設定を利用して、APIリクエストすることができるので、
複数のリクエスト処理でも共通化している部分があれば、インスタンス化することをお勧めします。

## 処理に割り込む
then か catchの処理が実行される前に、リクエストかレスポンスに処理を割り込ませることができます。
これにより、エラーハンドリングができたり、共通の処理を挟むことができます。

```js
const instance = axios.create()

//インスタンス.interceptors.(request or response).use(
//  request or responseに成功した時の処理, 
//  request or responseに失敗した時の処理,
//)

//リクエスト前に割り込む処理
instance.interceptors.request.use(
  config => console.log(config), 
  config => console.log(config),
)

//レスポンス前に割り込む処理
instance.interceptors.response.use(
  config => console.log(config), 
  config => console.log(config),
)
```
configの中身は```baseURL, date, header```などのaxiosで設定した、
またはデフォルトで設定されている情報がオブジェクトになって入っています。




## すこし実用的にすると

```apiClient.js
class ApiClient {
  static createInstance(){
    const instance = axios.create({
      baseURL: 'http://localhost:3333',
      headers: {
        'Content-Type': 'application/json',
      },
      timeout: 2000,
    })
    instance.interceptors.response.use(
      config => this.requestSuccess(config),
      config => this.requestFailure(config),
    )
    return instance
  }

  static requestFailure(config) {
    //request失敗した時の処理
    console.log('// REQUEST FAILURE', config)
    return Promise.reject(config)
  }
  static requestSuccess(config) {
    //request成功した時の処理
    console.log('// REQUEST SUCCESS', config)
    return config
  }

  static async get(url, callback, data) {
    const instance = this.createInstance()
    return await instance.get(url, data)
      .then(response => {
        return callback(response.data)
      })
      .catch(error => {
        return Promise.reject(error)
      })
  }

  static async post(url, callback, data){
    const instance = this.createInstance()
    return await instance.post(url, data)
      .then(response => {
       //returnして呼び出し元で使えるようにする
        return callback(response.data)
      })
      .catch(error => {
        return Promise.reject(error)
      })
  }
}   

export default ApiClient

```
### apiClient.jsを使う側
```js
import ApiClient from './apiClient' 

const postUser = () => {
  ApiClient.post(
    '/api/users', 
    //第二引数ではresponceを使って、stateを更新したり、reduxの非同期処理を行ったりする
    response => console.log(response),
    {name: 'test'}
  )
}
```

## まとめ
axiosはとてもシンプルで使いやすいライブラリで、es6で使えるfetchAPIを使うよりも運用しやすい、見通しのいいコードかけます。
RESTを使っての通信は今後もまだまだ主流だと思うのでお世話になることでしょう。

また、改善点がありましたらご指摘もお待ちしております。

## 追記（2024-2-9）

```ts
.then(response => {
  //returnして呼び出し元で使えるようにする
  return callback(response.data)
})
```
↑だとcallback箇所が汎用性いまいちだったのと、今っぽくclassでなくオブジェクトに直した版です。↓
ファイル系など、特殊なresponse, requestになるのもいちいち呼び出す側で書かないで済むようにメゾットとして追加してます。

```ts
export const axios = Axios.create({
  baseURL: 'http://localhost:3333',
  headers: {
    'Content-Type': 'application/json',
  },
})

export const API = {
  get: async (url: string, config?: AxiosRequestConfig): Promise<any> => {
    const client = axios
    const response = await client
      .get(url, config)
      .catch((err: AxiosError) => Promise.reject(err.response))
    return response?.data
  },
  getFile: async (
    url: string,
    config?: AxiosRequestConfig,
  ): Promise<{ data: BlobPart; headers: AxiosResponseHeaders }> => {
    const client = axios
    const response = await client
      .get(url, { ...config, responseType: 'blob' })
      .catch((err: AxiosError) => Promise.reject(err.response))
    return {
      data: response.data,
      headers: response.headers as AxiosResponseHeaders,
    }
  },
  post: async (
    url: string,
    data?: any,
    config?: AxiosRequestConfig,
  ): Promise<any> => {
    const client = axios
    const response = await client
      .post(url, data, config)
      .catch((err: AxiosError) => Promise.reject(err.response))
    return response?.data
  },
  postFile: async (
    url: string,
    data?: any,
    config?: AxiosRequestConfig,
  ): Promise<any> => {
    const client = axios
    const response = await client
      .post(url, data, {
        ...config,
        headers: { 'Content-Type': 'multipart/form-data;' },
      })
      .catch((err: AxiosError) => Promise.reject(err.response))
    return response?.data
  },
  put: async (
    url: string,
    data?: any,
    config?: AxiosRequestConfig,
  ): Promise<any> => {
    const client = axios
    const response = await client
      .put(url, data, config)
      .catch((err: AxiosError) => Promise.reject(err.response))
    return response?.data
  },
  patch: async (
    url: string,
    data?: any,
    config?: AxiosRequestConfig,
  ): Promise<any> => {
    const client = axios
    const response = await client
      .patch(url, data, config)
      .catch((err: AxiosError) => Promise.reject(err.response))
    return response?.data
  },
  delete: async (url: string, config?: AxiosRequestConfig): Promise<any> => {
    const client = axios
    const response = await client
      .delete(url, config)
      .catch((err: AxiosError) => Promise.reject(err.response))
    return response?.data
  },
}
```

### 使う側
```ts
import { API } from '@/lib/axios'

export const postLogout = (data: {token: string}): Promise<void> => {
  return API.post('/logout', data)
}
```