Fetch APIについて
この記事では、JavaScriptの**Fetch API**の簡単な使い方を紹介します。モチベーションとしては、「XMLHttpRequestだと記述が長い!」「コールバックが面倒!」です。
なお、この記事ではPromiseとawaitは出てきますが、asyncは出てきません。
Fetch APIとは
Fetch APIとは、XMLHttpRequestと同じでHTTPリクエストを発行する APIですが、XMLHttpRequestよりシンプルでモダンな APIです。
Fetch APIのfetchを使えば、下記のような簡単な呼び出しで HTTPリクエストを発行して結果を見ることができます。実際、F12ツール(DevTools)上でリクエストを送ってみると、
(await fetch("https://qiita.com/api/v2/items")).json();
こんな結果が返ってきます。
(20) [{…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}]
lastIndex: (...)
lastItem: (...)
0: {rendered_body: "<h2>...</h2>", body:"...", ...}
1: {rendered_body: "<h2>...</h2>", body:"...", ...}
<<中略>>
19: {rendered_body: "<h2>...</h2>", body:"...", ...}
length: 20
__proto__: Array(0)
XMLHttpRequestを使うよりはシンプルですし、jQuery.ajaxやaxios.getのようにライブラリに依存しません。
その代わり、実行可能なブラウザには制限ができてしまいます。私は、APIからデータを取得してJavaScriptでちょっとした加工をしたいときに使っています。
基本的には、これだけ知っていれば十分です。他に知る必要があるのはヘッダやメソッドの指定の仕方くらいでしょうか。
使ってみる
なにはともあれ、使ってみましょう。
このページを見ながら、F12キーか Ctrl+Shift+Iを押して F12ツール(DevTools)を開いて、コンソールを開いてください。
リクエストを発行する
fetchを使ってリクエストを送ってみます。
fetch("https://qiita.com/api/v2/items");
するとこんな値が返ってきます。
Promiseです。中身がぜんぜん見えませんね。
Promise {<pending>}
__proto__: Promise
[[PromiseStatus]]: "resolved"
[[PromiseValue]]: Response
Promiseの中身を見る
Promiseとはなにかを考える前に、とりあえず先頭にawaitをつけて実行してみましょう。
await fetch("https://qiita.com/api/v2/items");
すると、こんな感じのオブジェクトが返ってきます。
Responseです。さっきよりは中がちょっと見えます。
例えば、ステータスコードが200であることがわかります。
Response {type: "basic", url: "https://qiita.com/api/v2/items", redirected: false, status: 200, ok: true, …}
type: "basic"
url: "https://qiita.com/api/v2/items"
redirected: false
status: 200
ok: true
statusText: ""
headers: Headers {}
body: (...)
bodyUsed: false
__proto__: Response
bodyに値が入っていそうですが、クリックして展開してみるとReadableStreamと出てきます。
読み込めそうな何かだということしかわかりません。
body: ReadableStream;
locked: false;
__proto__: ReadableStream;
body の中身を見る
ReadableStreamのことは一旦忘れて、Responseについて調べてみましょう。
Response - Web API | MDNによると、arrayBuffer, blob, formData, json, textといったメソッドがあることがわかります。
今回の APIはJSONを返すことがわかっているので、jsonメソッドを使ってみましょう。
(await fetch("https://qiita.com/api/v2/items")).json();
ついにでデータまでたどり着くことができました。
(20) [{…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}]
lastIndex: (...)
lastItem: (...)
0: {rendered_body: "<h2>...</h2>", body:"...", ...}
1: {rendered_body: "<h2>...</h2>", body:"...", ...}
<<中略>>
19: {rendered_body: "<h2>...</h2>", body:"...", ...}
length: 20
__proto__: Array(0)
もう少し使ってみる
単にGETリクエストを送るだけではつまらないので、少しだけ深堀りしてみましょう。
ヘッダ
fetchの第2引数を使えば、好きなヘッダを付けてHTTPリクエスト送ることができます。
ヘッダ付きのHTTPリクエストを送るには下記のようにします。
await fetch(url, {
headers: {
Authorization: "Basic " + btoa("username" + ":" + "password"),
Accept: "application/json",
"Content-Type": "application/json;charset=utf-8"
}
});
Headersオブジェクトを生成して送ることもできます。
var headers = new Headers();
headers.set("Authorization", "Basic " + btoa("username" + ":" + "password"));
await fetch(url, {
headers
});
GET以外のリクエスト
fetchの第2引数を使えば、POSTリクエストやその他のリクエストを送ることができます。
dataに入っている文字列をそのままurlにPOSTで送るには、下記のようにします。
await fetch(url, {
method: "POST",
body: data
});
dataがJSONオブジェクトでurlにPOSTで送るには、下記のようにします。
await fetch(url, {
method: "POST",
body: JSON.stringify(data)
});
fetchのその他のオプション
詳しくはGlobalFetch.fetch() - Web API | MDNを見てください。
modeやcredential、cacheをよく使います。
F12ツール(DevTools)の特性
F12ツール(DevTools)は、とても良くできていてPromiseでも処理が終了していれば中身を見ることができます。[[PromiseValue]]となっているところに値が入っているので、クリックして展開して中身を見ることができます。
Promise {<pending>}
__proto__: Promise
[[PromiseStatus]]: "resolved"
[[PromiseValue]]: Response
type: "basic"
url: "https://qiita.com/api/v2/items"
redirected: false
status: 200
ok: true
statusText: ""
headers: Headers {}
body: (...)
bodyUsed: false
__proto__: Response
awaitは、この[[PromiseValue]]を、処理が終了するまで待ってから取得するための言語機能です。
Responseにmix-inされている、jsonなどのメソッドも実はPromiseを返します。Promiseから値を取り出すには下記のようにするのが正しいです。F12ツール(DevTools)で対話的に値を見るだけなら外側のawaitはなくてもOKです。
await (await fetch("https://qiita.com/api/v2/items")).json();
ReadableStream
Body - Web API | MDNによると、BodyはResponseへのmixinであり、Responseに対して arrayBuffer, blob, formData, json, textといったメソッドを提供するものです。
Body.bodyがReadableStreamですが、上記メソッドが呼ばれたときに変換する元データが入っている場所(元データを読み取り可能なストリーム)です。
ReadableStream - Web API | MDNに例があるように、ストリームを読み取りながら処理をしたり、読み取った前後になにか処理を実行したい場合に使うことができます。
Responseを扱うだけなら、Bodyのメソッドが覆い隠してくれるので、ReadableStreamを意識する必要はありません。
awaitを使わない方法
urlが文字列、optionsがオブジェクトのときawaitを使わずに同等の処理を書くなら下記のようになります。
fetch(url, options).then(response => {
// このブロックの中ではPromiseではなくて、通常の値として扱える
console.log(response);
return response; // returnしてもPromiseに包まれる
});
実用性はあまりないですが、こう書いたり、
fetch(url, options).then(response => console.log(response));
こう書くこともできます。
fetch(url, options).then(console.log);
もっと使いたい方へ
もっと進んだ使い方をしたい場合は、下記の記事が参考になります。
参考
この記事を書くにあたり、下記の記事を参考にしました。