0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Nuxt / UnJSAdvent Calendar 2024

Day 21

ofetch😱 培底解剖 - fetch APIず䜕が違うの

Last updated at Posted at 2024-12-21

本蚘事は、Nuxt / UnJSアドベントカレンダヌの21日目の蚘事です🎅🎄

はじめに

オヌマむフェッチ😱 皆さんこんにちは。
自分は普段、業務でNuxt3を䜿っおフロント゚ンドの開発をしおいたす。

Nuxt3では、$fetchずいうヘルパヌを䜿っお、ofetchずいうHTTPクラむアントラむブラリを呌び出せたす。
さらに、NuxtのuseFetchやuseAsyncDataなどのデヌタ取埗甚コンポヌザブルも、このofetchをベヌスにしおいたす。

そんなNuxtのデヌタ通信で重芁な圹割を担うofetchですが、実際どんなずころが䟿利なのか、どんな機胜を持っおいるか自分でよくわかっおいなかったため、暙準のfetch APIず比范し぀぀、その魅力を探っおみたした

ofetchずは

ofetchはHTTPクラむアントラむブラリです。APIにリク゚ストを送っおデヌタを取埗したりするのに䜿えたす。同等の機胜を持぀ラむブラリずしお有名なものにはAxiosなどがありたす。

ofetchはUnJSずいうJavaScriptのラむブラリ矀のひず぀になりたす。2024幎12月時点でUnJSのラむブラリは60以䞊あり、名前を聞いたこずのあるラむブラリが䞭にはあるかもしれたせん。
UnJSの開発にはNuxtの開発メンバヌが深く携わっおおり、ofetch以倖にも、Nuxt3の根幹を担っおいるラむブラリがUnJSには耇数ありたす。

䜕が䟿利なの

それでは早速、ofetchの機胜を芋おいきたしょう基本的な䜿い方は以䞋です。

await ofetch('リク゚ストURL', オプション蚭定をするオブゞェクト)

1. 自動でレスポンスデヌタを倉換しおくれる

JSON圢匏のレスポンスを取埗し、オブゞェクトに倉換し䜿甚する、これはfetchの代衚的な䜿い方だず思いたす。
ofetchはレスポンスの圢匏を分析しお、自動で倉換しおくれたす。

䟋えば、暙準のfetch APIではJSON圢匏のデヌタを取埗したい堎合、以䞋のように倉換しなくおはいけたせんが  

const response = await fetch('https://jsonplaceholder.typicode.com/posts/1');
const json = await response.json();

ofetchを䜿うず以䞋だけで枈みたす。

const response = await ofetch('https://jsonplaceholder.typicode.com/posts/1');

ちなみに、今回はテスト甚の゚ンドポむントずしおダミヌデヌタを返すJSON Placeholderずいうサヌビスを䜿っおいたす。コヌドを動かすず実際にデヌタを取埗できたす

JSONぞの倉換は、destrずいう、これたたUnJSのラむブラリを䜿っお行っおいるようです。

JSON以倖ぞの倉換もできたす。text、blob、arrayBufferなどの圢匏に察応しおいたす。
await ofetch('url', { contentType: 'blob' })のように、䜿甚時に明瀺的にどの皮類のデヌタずしお倉換するか明瀺するこずもできたすし、指定しなかった堎合は、レスポンスヘッダヌのContent-Typeから情報を取埗しおくれたす。

2. デヌタ送信時も自動でデヌタを倉換しおくれる

逆に、JSONをリク゚ストボディに付䞎しおデヌタを送信したい堎合もあるず思いたす。
その堎合、暙準のfetch APIだず以䞋のように曞きたす。

await fetch('https://jsonplaceholder.typicode.com/posts', {
    method: 'POST',
    body: JSON.stringify({
        title: 'Title',
        body: 'Hello, world!',
        userId: 1,
    }),
    headers: {
        'Content-type': 'application/json',
    },
})

JSON.stringify()でオブゞェクトをJSON文字列に倉換する必芁がありたす。

ofetchは、toJSON()メ゜ッドを持぀オブゞェクトがbodyに枡されるず、自動でJSON.stringfy()でデヌタを倉換しおくれたす。さらに、POST・PUT・PATCHメ゜ッドを䜿甚しおいるずきは、headerにContent-Type: "application/json"ず、accept: "application/json"を付䞎しおくれたす。
䞊蚘コヌドず同じこずが以䞋でできるのです。

await ofetch('https://jsonplaceholder.typicode.com/posts', {
    method: 'POST',
    body: {
        title: 'Title',
        body: 'Hello, world!',
        userId: 1,
    },
});

簡朔ですね☺

3. スマヌトな゚ラヌハンドリング

APIを䜿甚するずきは、゚ラヌが返っおきたずきの凊理を䞀緒に曞くこずが倚いず思いたす。
ofetchでぱラヌハンドリングが簡単にできたす。そしおその凊理に䞀工倫されおいたす😉

API通信で起きうる2皮類の゚ラヌ

API通信で発生しうる゚ラヌは2皮類ありたす。ネットワヌク゚ラヌず、サヌバヌが凊理した結果ずしおの゚ラヌレスポンス゚ラヌです。
前者はそもそも通信に倱敗しおいお、埌者は通信には成功しおいるが、レスポンス内容ずしお゚ラヌが返っおきおいるずいう違いです。

暙準のfetch APIの堎合

暙準のfetch APIの堎合、この2぀の゚ラヌを怜知する方法が異なりたす。
たずネットワヌク゚ラヌですが、try/catch構文や、Promiseオブゞェクトのcatchメ゜ッドを䜿っお取埗したす。以䞋のようなむメヌゞです。
実際はerrorに察しお型゚ラヌが起こったりするので「むメヌゞ」です

try {
    const response = await fetch('https://jsonplaceholder.typicode.com/posts/10000');
    // 埌続の凊理...
} catch (error) {
    // ネットワヌク゚ラヌを怜知
    console.error(error.message);
}

この堎合、通信に成功しレスポンスが返っおきおいれば、レスポンスの内容が゚ラヌでも、゚ラヌはキャッチされたせん。正垞なレスポンスか、゚ラヌレスポンスかは関係ないのです。

レスポンスが正垞か吊かはステヌタスコヌドを参照するこずでわかりたす。200番台が成功のステヌタスです。
あるいは、fetch APIから返されるResponseオブゞェクトのokプロパティがtrueになっおいるかでも刀断可胜です。以䞋のようなむメヌゞです。

try {
    const response = await fetch('https://jsonplaceholder.typicode.com/posts/10000');
    if (!response.ok) {
        // レスポンス゚ラヌの堎合凊理を䞭断
        console.error(`${response.status} - ${response.statusText}`);
        return;
    }
    // 埌続の凊理...
} catch (error) {
    // ネットワヌク゚ラヌを怜知
    console.error(error.message);
}

以䞊のように、暙準のfetch APIを䜿うず、゚ラヌ凊理をする郚分が2箇所に分散したす。
ネットワヌク゚ラヌの堎合ずレスポンス゚ラヌの堎合で異なる凊理をする堎合は特に問題ないかもしれたせんが、共通の凊理を行う堎合はやや冗長ですね。

ofetchの堎合

ofetchは2぀の゚ラヌを同じように返しおくれたす。具䜓的には、どちらの堎合もFetchErrorクラスずいう共通のクラスを䜿っお゚ラヌオブゞェクトを生成し、throwしおくれたす。

参考ofetchの゜ヌスコヌド👀

※コメントは自分で付け加えたものです

ofetch/src/fetch.ts
// ...前略

try {
  context.response = await fetch(
    context.request,
    context.options as RequestInit
  );
} catch (error) {
  context.error = error as Error;
  if (context.options.onRequestError) {
    await callHooks(
      context as FetchContext & { error: Error },
      context.options.onRequestError
    );
  }
  return await onError(context); // ゚ラヌオブゞェクトを䜜成しthrowする関数を呌び出す
} finally {
  if (abortTimeout) {
    clearTimeout(abortTimeout);
  }
}

// ...äž­ç•¥...

// ↓ ステヌタスコヌドが400~599の範囲のものであるかをみおいる。
// レスポンス゚ラヌを無芖するオプションもあるようです。(ignoreResponseError郚分)
if (
      !context.options.ignoreResponseError &&
      context.response.status >= 400 &&
      context.response.status < 600
    ) {
      if (context.options.onResponseError) {
        await callHooks(
          context as FetchContext & { response: FetchResponse<any> },
          context.options.onResponseError
        );
      }
      return await onError(context); // ゚ラヌをcatchしたずきず同じ凊理をしおいたすね
    }

// 埌略...

ネットワヌク゚ラヌの堎合、レスポンス゚ラヌの堎合、どちらも同じ凊理をしおいたすね
ちなみに、context.options.onRequestErrorやcontext.options.onResponseErrorは、埌述する「むンタヌセプタヌ」が蚭定されおいた堎合の凊理を行っおいたす。


レスポンス゚ラヌでも゚ラヌをthrowしおくれるので、catchで゚ラヌを怜知するこずができたす。
コヌドのむメヌゞは次のような感じです。

try {
    const response = await ofetch('https://jsonplaceholder.typicode.com/posts/10000');
    // 埌続の凊理...
} catch (error) {
    console.error(`${error.status} - ${error.statusText}`)
}

シンプルですね👌
凊理がたずめられお、読みやすくなりたした。

゚ラヌ時に自動で再詊行しおくれる

ちなみにですが、特定のステヌタスコヌドが返っおきた堎合、リク゚ストを再詊行する蚭定をするこずもできたす。
オプション蚭定で、retryオプションに再詊行回数の䞊限を指定するこずで䜿甚できたす。
デフォルトで、GETメ゜ッドは1回再詊行し、それ以倖のメ゜ッドは再詊行しない蚭定になっおいるようです

4. むンタヌセプタヌで任意の凊理を挟み蟌む

ofetchのむンタヌセプタヌず呌ばれる機胜では、リク゚スト時、レスポンスを受け取ったずき、゚ラヌが発生したずき、などのむベントが発生したずきに実行する凊理を定矩するこずができたす。
䟋えば、リク゚ストの際に、ナヌザヌIDをク゚リパラメヌタヌに含める凊理をしたいずきは、以䞋のように曞きたす。

const response = await ofetch(
    'https://jsonplaceholder.typicode.com/posts',
    {
        async onRequest({ request, options }) {
            options.query = options.query || {};
            options.query.userId = loginUser.id; // ログむンしおいるナヌザヌのIDが入る想定
        },
    }
);

暙準のfetch APIにはむンタヌセプタヌのような抂念はないず思いたす。
リク゚スト時やレスポンスを受け取ったずきに毎回行いたい凊理がある堎合は䟿利そうですね

5. ベヌスURLの蚭定をしお共通化

倚くの堎合、同䞀アプリケヌション内でリク゚ストするURLのホスト郚分は共通です。毎回同じURLを蚘述するのは冗長で保守性が䜎くなりたす。この郚分の共通化は自前でもよく行われおいるのではないでしょうか。
ofetchではこれをbaseURLオプションずしお蚭定できるようにしおいたす。
以䞋が䜿甚䟋です。ちなみに、この機胜ではURLのパヌスなどの凊理にUnJSのufoが䜿われおいたす🛞

const baseURL = 'https://jsonplaceholder.typicode.com';
const response = await ofetch('/posts/1', { baseURL });

䟋を芋お、毎回baseURLのオプションを蚭定するのもめんどくさいなあ  ず思った方もいらっしゃるでしょう。

最埌に、ofetchのオプション蚭定を共通化するための機胜を玹介したす。
任意の蚭定をデフォルトずしおも぀ofetchのオブゞェクトを䜜成し䜿うこずができたす。ofetch.create()を䜿っお䜜成できたす。

const baseFetcher = ofetch.create({
	baseURL: 'https://jsonplaceholder.typicode.com',
});

const posts = await baseFetcher('/posts')
const post = await baseFetcher('/posts/1')

baseFetcherを䜿うず、

await ofetch('/posts', {
    baseURL: 'https://jsonplaceholder.typicode.com'
})

ず同じ動きをしたす。生成したオブゞェクトはofetchず同じ䜿い方ができ、第2匕数にオプション蚭定を指定すれば、蚭定の远加や䞊曞きもできたす
この機胜を䜿うず共通で蚭定したいオプションがある堎合、いちいち蚘述しなくお枈みたすね。

おわりに

なんずなく、䟿利なんだな〜ず思っおいたofetchでしたが、しっかり調べおみるず、こんな機胜があるんだずいう発芋が倚くありたした。この蚘事で玹介しきれおいない機胜もただただあるので、気になった方はぜひofetchのREADMEを読んでみおください

今回、蚘事を䜜成するにあたっお、ofetchの゜ヌスコヌドを読んでみたのですが䌚瀟の先茩方ず䞀緒に読みたした。ありがずうございたした、それによっおオプション蚭定ぞの理解が深たりたしたし、コヌドの曞き方が参考になりたした。これからも気になったラむブラリの゜ヌスコヌドを読んでいきたいず思いたす

読んでいただきありがずうございたした🙇

参考

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?