かもメモ

自分の落ちた落とし穴に何度も落ちる人のメモ帳

Fetch API 試してみた。

恥ずかしながらFetchAPIたるものがあるのを知らなかったので、ざっくり試してみた。

Fetch API

Service Workersでも使えて、プレーンなJavaScriptXMLHttpRequestより強力で柔軟な操作ができるそうです。

GET

fetch(api_url, {method: 'GET'})
  .then((res) => {
    // resはResponseオブジェクト
    console.log( res );
    // 返されたBodyをjsonにしてPromise.resolve()する
    return res.json(); 
  })
  .then((data) => {
    // api_url から返されたデータ(json) 
    // 実際にデータを加工したり色々する
    console.log( data );
  })
  .catch((err) => {
    console.error( err );
  });

GETメソッドの場合は第二引数のmethod: 'GET'を省略することができます。

FetchAPIで最初で取得されるデータはResponseオブジェクトになっており、res.json()を実行することで返されたデータのBodyをJSON形式にしてPromise.resolve()に渡すようになっています。
JSON以外にも次のようにArrayBuffer, Blob, FormData, Stringにすることもできます。

  • ArrayBuffer: res.arrayBuffer()
  • Blob: res.blob()
  • FromData: res.formData()
  • String: res.text()

詳しくはドキュメント: Response - Web API | MDNを参照

POST

fetch(api_url, {
  method: 'POST',
  // 送信するデータ
  body: post_data
})
  .then((res) => res.json)
  .then((data) => console.log( data ))
  .catch((err) => console.error( err ));

POSTで送信するデータはbodyオプションに指定します。
methodPUT, DELETEにすればPUT, DELETEのリクエストも送ることが出来ます。

PUT

fetch(api_url, {
  method: 'PUT',
  body: post_data
})
  .then((res) => ...)

DELETE

fetch(api_url, {
  method: 'DELETE',
})
  .then((res) => ...)

クロスオリジン (クロスドメイン)の送信

クロスオリジンで通信を行いたい時はmodeオプションにcorsを指定します。

fetch(api_url, {
  mode: 'cros'
}).then((res)...)

mode オプション

  • same-origin: *default 同じドメイン(same origin)のみ許可。クロスオリジンは接続エラー
  • cors: クロスオリジンを許可。レスポンスは表示できるヘッダが Cache-Control, Content-Language, Content-Type, Expires, Last-Modified, Pragmaに限定される

    cors and basic responses are almost identical except that a cors response restricts the headers you can view to Cache-Control, Content-Language, Content-Type, Expires, Last-Modified, and Pragma.

  • no-cors: CORSヘッダーを持たないopaqueレスポンスを返すオリジン用。接続ができない場合にエラーとならずに空のレスポンスが返される。

    no-cors is intended to make requests to other origins that do not have CORS headers and result in an opaque response, but as stated, this isn't possible in the window global scope at the moment.

no-corsについてはイマイチ理解が出来ていないのですが、クロスオリジンにリクエストを送る際はmode: 'cors'としておけば良さそうです。

詳しくは Request.mode - Web API | MDNResponse Types Introduction to fetch() | web | Google Developersを参照

クレデンシャル (認証情報: credentials)

クッキーなどのクレデンシャルを送る場合はcredentialsオプションをincludeにする

fetch(api_url, {
  credentials: 'include'
}).then((res) => ... )

credentials オプション

  • omit: *default リクエストにクレデンシャル(認証情報)を含まない
  • same-origin: 同一オリジンの場合だけクレデンシャル(認証情報)を送る
  • include: クレデンシャル(認証情報)の入ったリクエストを送る

cf. Sending_a_request_with_credentials_included | Fetch 概説 | MDN

カスタムヘッダー

クロスオリジン・クレデンシャル以外にもcache, header, redirect, referrerリクエストのヘッダーのオプションが用意されています。

e.g. JSONで指定したヘッダー情報を追加

fetch(api_url, {
  header: {
    'Content-Type': 'application/json'
  }
}).then((res) => ... )

Header情報はHeaderインターフェースで色々と独自な設定が可能なようです。
cf. Headers | FetchAPI 概説 | MDN

対応ブラウザとPolyfill

fetchAPI - I can use
Can I use... Support tables for HTML5, CSS3, etc

この記事を書いた2019年1月時点では、IE11を含めたIEと、IE Mobile, Blackberry といった幾つかのモバイルブラウザで未対応をなっているようです。
Polyfillが用意されているのでこれらの非対応サポートブラウザで使用するにはこちらを利用するのが良さそうです。

Browser Support

babelしてみた

babel7で自動的にPolyfillがロードされる設定でFetchAPIが非対応ブラウザーようにバベれるか一応試してみました。

babel.config.js

const presets =  [
  ["@babel/preset-env", {
    "modules": false,
    "targets": ["ie > 10"],
    "useBuiltIns": "usage",
  }]
];
module.exports = { presets }

index.js

fetch('http://localhost:3000', {
  method: 'GET'
})
.then((res) => res.json())
.then((data) => console.log(data))
.catch((err) => console.error(err));

👇$ yarn babel バベる

bandle.js

// 略
fetch('http://localhost:3000', {
  method: 'GET'
}).then(function (res) {
  return res.json();
}).then(function (data) {
  return console.log(data);
}).catch(function (err) {
  return console.error(err);
});

PromiseのPolyfillもロードされませんでした。
babel7デフォルトの@babel/preset-envではFetchAPIはPolyfillされないようです。
FetchAPIを使っているプロジェクトで非サポートブラウザ対応をする場合は配布されているPolyfillを使用するのが良さそうです。(babelで読み込ませられるのかな?)

感想

thenを二重にしないといけないのが最初ちょっとキモかったですがjQueryを使わない場合は、生JSのXMLHttpRequestで書くよりタイプ数も少なくて済むし見通しもよいので良さそうだと思いました。(生JS...


[参考]

[asin:B07L1GJZ1F:detail]