axiosラッパーでAPI通信を一本化しよう！

# はじめに

APIを叩く処理が多くなると、都度 `axios.post` や `axios.get` を書くのが煩雑になりがち。
また、プロジェクトによっては下記のようなケースにも対応する必要が出てきます。

* Authorizationヘッダーの追加
* FormDataの送信
* Digest付きのリクエスト
* ファイルのチャンクアップロード（Content-Rangeヘッダー）
* 自己署名証明書のAPI接続（SSL無視）

そんな時に便利なのが **共通処理をラップしたHTTPクライアントクラス**。
今回はそれを `Postman` というクラスとして実装してみました。

**補足：なぜSSLを無視する？**

https://qiita.com/oharu121/items/49504ea4a6126936431d

# 実装概要

```ts
import axios, { AxiosResponse } from "axios";
import https from "https";

const agent = new https.Agent({
  rejectUnauthorized: false, // 自己署名証明書を無視
});

class Postman {
  // メソッドは後述
}

export default new Postman();
```

共通設定として、`https.Agent` を使って SSL エラーを回避しています。
あとは `axios.post`, `axios.get`, `axios.put`, `axios.delete` をラップするメソッドをそれぞれ定義しています。

# GETリクエスト（Authorization対応）

```ts
public async get(url: string, token?: string): Promise<AxiosResponse> {
  const headers: { [key: string]: string } = {
    "Content-Type": "application/json",
  };

  if (token) headers["Authorization"] = `Bearer ${token}`;

  return await axios.get(url, {
    headers: headers,
    httpsAgent: agent,
  });
}
```
* `Content-Type` を `application/json` に固定
* `token` が渡された場合、`Authorization: Bearer {token}` を付与
* `httpsAgent` を設定することで自己署名証明書のAPIにも対応

**🧪 使用例**

```ts
const res = await Postman.get("https://api.example.com/profile", "your-token");
console.log(res.data);
```

# DELETEリクエスト（Authorization対応）

```ts
public async delete(url: string, token?: string): Promise<AxiosResponse> {
  const headers: { [key: string]: string } = {
    "Content-Type": "application/json",
  };

  if (token) headers["Authorization"] = `Bearer ${token}`;

  return await axios.delete(url, {
    headers: headers,
    httpsAgent: agent,
  });
}
```

クエリパラメータ付きGETリクエスト

```ts
public async getWithParams(
  url: string,
  params: object,
  token?: string
): Promise<AxiosResponse> {
  const headers: { [key: string]: string } = {
    "Content-Type": "application/json",
  };

  if (token) headers["Authorization"] = `Bearer ${token}`;

  return await axios.get(url, {
    headers: headers,
    params,
    httpsAgent: agent,
  });
}
```

* `params` オプションはURL末尾のクエリ文字列として変換されます（例：`?page=2&limit=10`）
* 認証トークン付きでパラメータ付きGETを行う場合に便利

**🧪 使用例**

```ts
const res = await postman.getWithParams(
  "https://api.example.com/search",
  { q: "axios", limit: 5 },
  "your-token"
);
console.log(res.data);
```

# 基本のPOSTリクエスト（認証対応）

```ts
public async post(url: string, payload: object, token?: string): Promise<AxiosResponse> {
  const headers: Record<string, string> = {
    "Content-Type": "application/json",
  };
  
  if (token) headers["Authorization"] = `Bearer ${token}`;

  return await axios.post(url, payload, {
    headers,
    httpsAgent: agent,
  });
}
```

* 通常のJSON形式のPOST
* 認証トークンはオプション引数で対応

# Digestヘッダー付きPOST

```ts
public async postWithDigest(
  url: string,
  payload: object,
  digest: string,
  token?: string
): Promise<AxiosResponse> {
  const headers = {
    "Content-Type": "application/json",
    Digest: `sha=${digest}`,
  };
  
  if (token) headers["Authorization"] = `Bearer ${token}`;

  return await axios.post(url, payload, {
    headers,
    httpsAgent: agent,
  });
}
```

* `Digest` ヘッダーを追加して、セキュアなAPI設計に対応

# チャンクアップロード（octet-stream + Range指定）

```ts
public async putOctet(
  url: string,
  chunk: Buffer,
  contentLength: number,
  offset: number,
  end: number,
  fileSize: number,
  digest: string,
  token?: string
): Promise<AxiosResponse> {
  const headers = {
    "Content-Type": "application/octet-stream",
    "Content-Length": contentLength.toString(),
    "Content-Range": `bytes ${offset}-${end - 1}/${fileSize}`,
    Digest: `sha=${digest}`,
  };
  if (token) headers["Authorization"] = `Bearer ${token}`;

  return await axios.put(url, chunk, {
    headers,
    transformRequest: [(data) => data],
    httpsAgent: agent,
  });
}
```

* `Content-Range` で分割アップロード対応
* `Buffer` をそのままPUT

# FormData送信

```ts
public async postWithFormData(
  url: string,
  formData: FormData,
  token?: string
): Promise<AxiosResponse> {
  const headers: Record<string, string> = {};
  if (token) headers["Authorization"] = `Bearer ${token}`;

  return await axios.post(url, formData, {
    headers,
    httpsAgent: agent,
  });
}
```

* フロントエンドから `FormData` をそのまま渡せる

# クエリパラメータを含むPOST

```ts
public async postWithParams(
  url: string,
  params: object,
  payload: object
): Promise<AxiosResponse> {
  return await axios.post(url, payload, {
    headers: { "Content-Type": "application/json" },
    params,
    httpsAgent: agent,
  });
}
```

* `params` はURL末尾のクエリとして使われます

# URLエンコード形式でPOST

```ts
public async postWithEncodedString(
  url: string,
  payload: object
): Promise<AxiosResponse> {
  return await axios.post(url, payload, {
    headers: { "Content-Type": "application/x-www-form-urlencoded" },
    httpsAgent: agent,
  });
}
```

* `application/x-www-form-urlencoded` なAPI用
* payloadは事前に`URLSearchParams`などで変換推奨

# おわりに

**🎯 まとめ**

この `Postman` クラスを使うことで…

* axiosの重複コードを排除できる
* 認証、Digest、FormDataなどの細かい要件に対応できる
* 将来の共通処理（リトライ処理やログ出力）も1箇所で管理可能

業務でAPIを叩く機会が多い方や、セキュアなアップロード処理を扱う方にとって、こうしたラッパークラスはコードの可読性と保守性を大きく改善してくれます。

今後はここにリトライロジックやログ収集の処理なども追加していく予定です。