👏

REST APIを型安全に呼び出し・状態管理ができるReact Hook「useAspidaCaller」を書いた

2022/04/25に公開

useAspidaCallerという、REST APIを型安全に呼び出し・状態管理ができるReact Hooksライブラリを書きました。

https://github.com/TeXmeijin/use-aspida-caller

対象ユーザーはaspidaを使ってREST APIを型安全に実行しており、かつReactを使っている方です。

https://github.com/aspida/aspida

インストール

npm i use-aspida-caller
yarn add use-aspida-caller

※最初@texmeijin/use-aspida-callerという命名でしたがよりシンプルなものに切り替えました

特長① ローディングやエラーなどのAPI呼び出しに関連する状態管理ができる

useAspidaCallerを使うと、POSTやPUTといった更新系のAPIを型安全に叩けるとともに、ローディングやエラーといった状態管理も宣言的に実装できます。

サンプルコード
const Sample = () => {
  const {
    put,
    isPutting,
    isPutSuccessful,
    putError,
  } = useAspidaCaller(client.people._id(1));

  const handlePutClick = async () => {
    await put({ body: { name: "hoge" } }).catch((err) => null);
  };

  return (
      <Form>
        <Button isLoading={isPutting} onClick={handlePutClick}>Put Value</Button>
        {
          putError && <ErrorMessage>{putError.message}</ErrorMessage>
        }
        {
          isPutSuccessful && <SuccessMessage>送信に成功しました</SuccessMessage>
        }
      </Form>
  );
};

useAspidaCallerにaspidaで定義したAPIオブジェクトを渡すと、呼び出しメソッドとローディング、成功フラグ、エラーオブジェクトが返ってきます。各種フラグとエラーオブジェクトは呼び出しメソッドの実行に伴って内容が変化します。

たとえばフォームのSubmit時にリクエストを投げるため、ローディングやエラーをそれぞれuseStateを使ったステートで管理している場面では、コードをより宣言的に、かつシンプルにすることができます

特長② aspidaを用いることでリクエストおよびレスポンスが型安全になる

前節の特長だけだとreact-useのuseAsyncFnなどと特長が変わりません。

しかし本ライブラリの特筆すべきところはaspidaを用いることでリクエストおよびレスポンスを型安全にしている点です。

aspidaを使って、/people/{id}のAPIに対してPUTリクエストが型定義されている場合、

export type Methods = {
  put: {
    reqBody: {
      name: string;
    };
    resBody: {
      status: number;
    };
  };
};

useAspidaCallerから返されるputメソッドは指定した通りのリクエストボディの型で呼び出さなければ型エラーになります。

  const handlePutClick = async () => {
    //                ↓ここがname以外のキー名だったり、string以外を渡しているとエラーとなる
    await put({ body: { name: "hoge" } }).catch((err) => null);
  };

特長③ aspidaで定義しているHTTPアクションによって戻り値が異なる

前節の例ではputメソッドを定義しているため、useAspidaCallerの戻り値もput関連の変数でした。

  const {
    put,
    isPutting,
    isPutSuccessful,
    putError,
  } = useAspidaCaller(client.people._id(1));

しかし、HTTPアクションは同一のエンドポイントに対してPUTだけでなくPOSTやDELETE、GETなど定義可能です。

そこで、もとのAPI定義にたとえばPOSTが定義されている場合、useAspidaCallerの戻り値もpost関連の変数で返ってくるように実装されています。

export type Methods = {
  post: {
    reqBody: {
      name: string;
    };
    resBody: {
      status: number;
    };
  };
};
  const {
    post,
    isPosting,
    isPostSuccessful,
    postError,
  } = useAspidaCaller(client.people._id(1));

もとのAPI定義に、POST、PUT、DELETEが定義されている場合、useAspidaCallerの戻り値にはそれら全てに応じた戻り値が返ってきます。

export type Methods = {
  post: {
    reqBody: {
      name: string;
    };
    resBody: {
      status: number;
    };
  };
  put: {
    reqBody: {
      name: string;
    };
    resBody: {
      status: number;
    };
  };
  delete: {
    reqBody: {};
    resBody: {
      status: number;
    };
  };
};
  const {
    // POSTの呼び出しメソッド、および状態変数
    post,
    isPosting,
    isPostSuccessful,
    postError,
    // PUTの呼び出しメソッド、および状態変数
    put,
    isPutting,
    isPutSuccessful,
    putError,
    // DELETEの呼び出しメソッド、および状態変数
    deleteApi,
    isDeleting,
    isDeleteSuccessful,
    deleteError,
  } = useAspidaCaller(client.people._id(1));

たとえば、もとのAPI定義にPUTが無い場合はputisPuttingなどを利用することはできず、使おうとしても型エラーになります。

仕様の解説

対応しているHTTPアクション

GET/POST/PUT/DELETE/PATCHに対応しています。

戻り値の解説

putを例に説明します。

キー名 内容
put useAspidaCallerに渡しているエンドポイントに対してPUTリクエストを飛ばす。引数は型定義の通り。
isPutting putメソッドを実行している間trueになる。そのため初期値はfalse
isPutSuccessful putメソッドを実行し、1度でも成功すればtrueとなる。初期値はfalse
putError putメソッドを実行し、失敗知ればそのエラー内容が入る。初期値はundefined

なお、DELETEメソッドの場合のみ、deleteApiという名前で実行用のメソッドが返ってきます。

詳細な挙動

テストコードを読むことで詳細な挙動を知ることができます。
https://github.com/TeXmeijin/use-sender-aspida-hooks/blob/2273ca9a4bdf965e2c02bbd2f1b62f1a7bf4f5fa/src/tests/useAspidaCaller.spec.ts

Discussion