freeeAPIのエラーハンドリングをしよう

### はじめに
現在、freee・ヤマトの連携アプリをつくっています。
アプリの詳細はこちらに記載していますので、よかったら覗いていっていただけると嬉しいです。


https://qiita.com/bonbons_ramune/items/818fa903e9ea0ac73230


### freeeAPI関連で苦戦したこと

アプリからfreeeAPIを叩く際に、一番苦戦したのが、エラーハンドリングでした。
エラーのstatus codeによって、返すべきエラーメッセージの箇所が異なるため、細かくハンドリングする必要がありました。

今回はその実装手順を記載していきます。

### freeeAPIの検証方法

まずは、postmanからfreeeのAPIをコールできるように準備します。
以下の記事がとても参考になりました。

https://www.cdatablog.jp/entry/howtousefreeeapi

APIリファレンスのページからOpen APIのスキーマを入手でき、これを使って設定を進めるため超簡単にAPI検証を始めることができます。


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1935678/a0cff364-70eb-9602-c883-c9e253072db8.png)

token取得がエラーになる場合は、callbackURLがpostmanとfreeeで一致していない可能性があります。
freeeのコールバックURLが、
`https://oauth.pstmn.io/v1/browser-callback` になっているかを確認してください。
私はここをあわせることでエラーがなくなり、上手くいきました。


### company_idを取得する
事業所の色々な情報をGETするには、company_idを指定する必要があります。
company_idは`api/1/companies`を叩くことで取得が可能です。

![スクリーンショット 2022-03-11 16.03.19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1935678/4aab4a42-ec74-420f-5047-cd2bf8e9fed1.png)


### 勘定科目の一覧を取得する
取得したcompany_idをparamsのcompany_idのVALUEに記載します。
※company_id以外のkeyはチェックを外してOKです。

Sendを押下すると、ステータス200で勘定科目の一覧が返ってきます。

![スクリーンショット 2022-03-11 16.20.58.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1935678/4a7a6ef2-0be1-8d29-820b-62407d1a19cc.png)




### 実際にエラーを起こしてみる
さてここからが本題です。
勘定科目の閲覧権限がない状態でAPIリクエストしてみると、以下のように返ってきます。


```json
{
    "status_code": 401,
    "errors": [
        {
            "type": "status",
            "messages": [
                "アクセス権限がありません。",
                "company_admin",
                "api/v1/account_items",
                "index",
                "このAPIにアクセスしたい場合、事業所の管理者にご確認ください。"
            ],
            "codes": [
                "user_do_not_have_permission"
            ]
        }
    ]
}
```

### 実際のエラーハンドリング
何のエラーが起きているのかをユーザーに知らせるためにトーストでエラー内容を反映させていきます。
chakraUIのtoastを使うと比較的簡単にトーストの表示ができます。

https://chakra-ui.com/docs/components/feedback/toast

```ts

  /**
   * GET /account_items
   */
  async getAccountItems(
    userId: string,
    companyId: number
  ): Promise<GetAccountItems | AccountItem[]> {
    let response;
    response = await this.get<any>(userId, "api/1/account_items", companyId);

    return response;
  }
```


```ts
export const getFreeeAccountItems = async (companyId: number) => {
  const response: any = await FunctionAPI.getAccountItems(companyId);
  switch (response.status_code) {
    case 400:
      switch (response.errors?.length) {
        case 1:
          return `${response?.errors[0].messages[0]}(${response.status_code})`;

        case 2:
          return `${response?.errors[1].messages[0]}(${response.status_code})`;
      }
      break;
    case 401:
      return `${response?.errors[0].messages[0]}${response.errors[0].messages[3]}(${response.status_code})`;
    case 404:
      return `${response?.errors[1].messages[0]}(${response.status_code})`;
  }
  return response.account_items;
};
```

```ts
import { useCallback } from "react";
import { useToast } from "@chakra-ui/react";
import { ToastMessage } from "src/components/molecules/ToastMessage";

export const useAlert = () => {
  const toast = useToast();

  const handleToast = useCallback((massage, status) => {
    toast({
      position: "top-right",
      isClosable: true,
      duration: 3000,
      render: () => toastTheme(massage, status),
    });
  }, []);

  const toaster = useCallback((message, status) => {
    handleToast(message, status);
  }, []);

  const toastTheme = (message: string, status: string) => (
    <ToastMessage status={status} message={message} />
  );

  return { toaster };
};

```

```ts
  const handleGetAccountItems = async () => {
    const response = await getFreeeAccountItems(companyId);
    typeof response === "string" ? toaster(response, "error") : toaster("取得に成功しました", "success");
  };
```


toastのメッセージに、responseのmessageをのっけたら完成です！


![スクリーンショット 2022-03-11 16.55.42.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1935678/db39ae6e-0e71-d4f8-dade-82cbd97c7857.png)


### おわりに

toastのコンポーネントやカスタムフックの作成などをはじめ、チームメンバーのエンジニアが実装をリードしてくれました。
独学でアプリを作っているとわけがわからず、つまってしまうこともありますが、チーム開発だとわいわいしながら相互にサポートしあえるのでとてもよかったです。