見出し画像

「Fetch API」についてまとめる

今まで、ajax通信にはHTTPクライアント「axios」を使っていたが
最近はIE11をサポートしなくて良くなってきたので、
ブラウザでnativeでサポートされているFetch APIを使うことが多くなってきた。

挙動にちょっと癖があるのもあり、この際 きちんと詳細まで理解しておこうと思ったのでそのまとめ。


Fetch API

・(ネットワーク越しの通信を含む) リソース取得のためのインターフェースを提供。MLHttpRequestより簡単で便利
・fetch() は Promiseを返す
・レスポンスが404や500でもrejectを返さない。ネットワークエラーの時にのみrejectを返す

// 基本的な使い方

// function fetch(input: RequestInfo, init?: RequestInit | undefined): Promise<Response>

fetch('http://example.com/api')
.then(response => response.json())
.then(data => console.log(data))

fetch()でResponseオブジェクトを含むpromiseを返す。
このままでは、取得したデータを取得出来ないので、Responseオブジェクトが持つjsonメソッドでパースしてから使用する。

// ネットワークエラーの時しかrejectしない!
// 400番や500番の時は、 resolvedになる

fetch('http://example.com/api')
.then(response => response.json())
.then(data => console.log(data))
.catch(e => console.log(e)

エラーハンドリングにちょっと癖があり、
「ネットワークのエラーや、何かがリクエストの完了を妨げた場合のみ」
ということなので、上記だと漏れてしまう。

なので上記の記事を参考にさせてもらった。

fetch('/api')
    .then((response) => {
    // 400, 500系のエラーをキャッチ
    console.log(response.ok);
    console.log(response.status);
    console.log(response.statusText);
    if (!response.ok) {
     throw new Error(response.statusText);
    }
    return response;
    })
    .then((response) => {
    // jsonにパース
    return response.json();
    })
    .then((data) => {
    // 成功の時にする処理
    console.log(data);
    })
    .catch((e) => {
    // 失敗した時にする処理
    // 400, 500系のエラーこちらにキャッチ
    // ここでネットワークエラーもキャッチ
    console.log('Error: ' + e.message);
});

使いやすいようにアレンジして、ラッパーを作成。
resolveでもrejectでも、どちらでも同じ形式のobjectを返すようにすれば
使いやすそう。

/**
* fetch APIの ラッパー
*/

type Props = {
 url: string;
 init: RequestInit;
 debug?: boolean;
};

export function fetchApi<T>({
 url,
 init,
 debug = false,
}: Props): Promise<{ data: T | null; fetchError: string | null }> {
 return fetch(url, init)
   .then((response) => {
     if (debug) {
       console.log(
         '[ok]: ' + response.ok,
         '[status]: ' + response.status,
         '[text]: ' + response.statusText
       );
     }
     if (!response.ok || response.status === 0) {
       throw new Error(response.statusText); // 0, 400, 500 errors
     }
     return response.json();
   })
   .then((data: T) => {
     return {
       data: data,
       fetchError: null,
     };
   })
   .catch((e) => {
     return {
       data: null,
       fetchError: e.message,
     };
   });
}
// usage

import { fetchApi } from '@/utils/fetchApi';

  const onSubmit = async (data: Params) => {
   const result = await fetchApi<dataType>({
     url: '/contact_api/',
     init: {
       method: 'POST',
       body: JSON.stringify(data),
       headers: {
         'Content-Type': 'application/json',
         'X-From': location.origin,
       },
     },
   });
   console.log(result);
 };