くらげになりたい。

くらげのようにふわふわ生きたい日曜プログラマなブログ。趣味の備忘録です。

unjs/ofetchでResponseErrorを共通化する

Axiosからunjs/ofetchに移行する際に、
例外を共通化・独自クラスにしたいなと思い、
いろいろ調べてみたときの備忘録(*´ω`*)

onResponseErrorを利用すればOK

ofetch.create()を使って、共通的なレスポンスエラーに対し、
onResponseErrorで設定してやればいいっぽい。

import { ofetch, type $Fetch, type FetchOptions } from "ofetch";

const $Fetch $fetch = ofetch.create({
  onResponseError: ({ response, error, request, options }) => {
    // Raw Responseから必要な情報を取得する
    const number statusCode = response.status;
    const string statusText = response.statusText;
    const any responseBody = response._data;
    
    // 共通エラーでwrap
    throw new MyError({ statusCode, statusText, responseBody });
  }
});

レスポンスボディはresponse._dataで取得

エラー時のレスポンスの中身はresponse._dataで取得する

responseは素のFetch APIのRequest/Responseなので、
json()text()を使えばいいかと思ったけど、
Body is unusableBody used already forなどのエラーになる。。

onResponseError: ({ response, error, request, options }) => {
  // json()やtext()だとエラーになる
  const any responseBody = response.json();
  // or
  const any responseBody = response.text();
}

responseは一度使われるとresponse.bodyUsedtrueになり、
再度利用するとErrorがthrowされるが、
ofetch側でonResponseErrorを呼ぶ前にresponseを利用しているため。

ofetch側で読み込んだデータはresponse._dataに格納されるため、
そちらを使うようにする。

ofetchでのonResponseErrorの呼ばれ方

ofetchの該当箇所はこんな感じになってて、
onResponseErrorはレスポンスエラーの加工のみ。
onResponseErrorでthrowしなくても、握りつぶすことはできないっぽい。

const $fetchRaw: $Fetch["raw"] = async function $fetchRaw(
  _request,
  _options = {}
) {
  // ... 略
  if (
    !context.options.ignoreResponseError &&
    context.response.status >= 400 &&
    context.response.status < 600
  ) {
    if (context.options.onResponseError) {
      await context.options.onResponseError(context as any);
    }
    return await onError(context);
  }

  return context.response;
};

const $fetch = async function $fetch(request, options) {
  const r = await $fetchRaw(request, options);
  return r._data;
} as $Fetch;

以上!! これでちょっと共通化が楽になる(*´ω`*)

参考にしたサイトさま