JavaScriptのFetch API について

# Fetch APIについて

この記事では、JavaScriptの<strong>[Fetch API](https://developer.mozilla.org/ja/docs/Web/API/Fetch_API)</strong>の簡単な使い方を紹介します。モチベーションとしては、「XMLHttpRequestだと記述が長い！」「コールバックが面倒！」です。

![hac2019_2本目.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/555234/17ec9eda-c19a-36cb-5704-66cd37780348.png)

なお、この記事ではPromiseとawaitは出てきますが、asyncは出てきません。

## Fetch APIとは

Fetch APIとは、[XMLHttpRequest](https://developer.mozilla.org/ja/docs/Web/API/XMLHttpRequest)と同じでHTTPリクエストを発行する APIですが、XMLHttpRequestよりシンプルでモダンな APIです。
Fetch APIの[fetch](https://developer.mozilla.org/ja/docs/Web/API/WindowOrWorkerGlobalScope/fetch)を使えば、下記のような簡単な呼び出しで HTTPリクエストを発行して結果を見ることができます。実際、F12ツール(DevTools)上でリクエストを送ってみると、

```javascript
(await fetch("https://qiita.com/api/v2/items")).json();
```

こんな結果が返ってきます。

```javascript
(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を使ってリクエストを送ってみます。

```javascript
fetch("https://qiita.com/api/v2/items");
```

するとこんな値が返ってきます。
**Promise**です。中身がぜんぜん見えませんね。

```javascript
Promise {<pending>}
  __proto__: Promise
  [[PromiseStatus]]: "resolved"
  [[PromiseValue]]: Response
```

### Promiseの中身を見る

Promiseとはなにかを考える前に、とりあえず先頭に**await**をつけて実行してみましょう。

```javascript
await fetch("https://qiita.com/api/v2/items");
```

すると、こんな感じのオブジェクトが返ってきます。
**Response**です。さっきよりは中がちょっと見えます。
例えば、ステータスコードが200であることがわかります。

```javascript
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**と出てきます。
読み込めそうな何かだということしかわかりません。

```javascript
body: ReadableStream;
locked: false;
__proto__: ReadableStream;
```

### body の中身を見る

ReadableStreamのことは一旦忘れて、Responseについて調べてみましょう。
[Response \- Web API \| MDN](https://developer.mozilla.org/ja/docs/Web/API/Response)によると、**arrayBuffer**, **blob**, **formData**, **json**, **text**といったメソッドがあることがわかります。
今回の APIはJSONを返すことがわかっているので、jsonメソッドを使ってみましょう。

```javascript
(await fetch("https://qiita.com/api/v2/items")).json();
```

ついにでデータまでたどり着くことができました。

```javascript
(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リクエストを送るには下記のようにします。

```javascript
await fetch(url, {
  headers: {
    Authorization: "Basic " + btoa("username" + ":" + "password"),
    Accept: "application/json",
    "Content-Type": "application/json;charset=utf-8"
  }
});
```

Headersオブジェクトを生成して送ることもできます。

```javascript
var headers = new Headers();
headers.set("Authorization", "Basic " + btoa("username" + ":" + "password"));
await fetch(url, {
  headers
});
```

### GET以外のリクエスト

fetchの第2引数を使えば、POSTリクエストやその他のリクエストを送ることができます。

dataに入っている文字列をそのままurlにPOSTで送るには、下記のようにします。

```javascript
await fetch(url, {
  method: "POST",
  body: data
});
```

dataがJSONオブジェクトでurlにPOSTで送るには、下記のようにします。

```javascript
await fetch(url, {
  method: "POST",
  body: JSON.stringify(data)
});
```

### fetchのその他のオプション

詳しくは[GlobalFetch\.fetch\(\) \- Web API \| MDN](https://developer.mozilla.org/ja/docs/Web/API/WindowOrWorkerGlobalScope/fetch#Parameters)を見てください。
**mode**や**credential**、**cache**をよく使います。

### F12ツール(DevTools)の特性

F12ツール(DevTools)は、とても良くできていてPromiseでも処理が終了していれば中身を見ることができます。[[PromiseValue]]となっているところに値が入っているので、クリックして展開して中身を見ることができます。

```javascript
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です。

```javascript
await (await fetch("https://qiita.com/api/v2/items")).json();
```

### ReadableStream

[Body \- Web API \| MDN](https://developer.mozilla.org/ja/docs/Web/API/Body)によると、BodyはResponseへのmixinであり、Responseに対して arrayBuffer, blob, formData, json, textといったメソッドを提供するものです。

Body.bodyがReadableStreamですが、上記メソッドが呼ばれたときに変換する元データが入っている場所(元データを読み取り可能なストリーム)です。
[ReadableStream \- Web API \| MDN](https://developer.mozilla.org/ja/docs/Web/API/ReadableStream)に例があるように、ストリームを読み取りながら処理をしたり、読み取った前後になにか処理を実行したい場合に使うことができます。

Responseを扱うだけなら、Bodyのメソッドが覆い隠してくれるので、ReadableStreamを意識する必要はありません。

### awaitを使わない方法

urlが文字列、optionsがオブジェクトのときawaitを使わずに同等の処理を書くなら下記のようになります。

```javascript
fetch(url, options).then(response => {
  // このブロックの中ではPromiseではなくて、通常の値として扱える
  console.log(response);
  return response; // returnしてもPromiseに包まれる
});
```

実用性はあまりないですが、こう書いたり、

```javascript
fetch(url, options).then(response => console.log(response));
```

こう書くこともできます。

```javascript
fetch(url, options).then(console.log);
```

## もっと使いたい方へ

もっと進んだ使い方をしたい場合は、下記の記事が参考になります。

- [GlobalFetch\.fetch\(\) \- Web API \| MDN](https://developer.mozilla.org/ja/docs/Web/API/WindowOrWorkerGlobalScope/fetch#Parameters)
- [Fetch - 現代の JavaScript チュートリアル](https://ja.javascript.info/fetch)

## 参考

この記事を書くにあたり、下記の記事を参考にしました。

- [Fetch API \- Web API \| MDN](https://developer.mozilla.org/ja/docs/Web/API/Fetch_API)
- [Fetch を使う \- Web API \| MDN](https://developer.mozilla.org/ja/docs/Web/API/Fetch_API/Using_Fetch)
- [GlobalFetch\.fetch\(\) \- Web API \| MDN](https://developer.mozilla.org/ja/docs/Web/API/WindowOrWorkerGlobalScope/fetch#Parameters)
- [fetch,async/await で Ajax 通信 \- Qiita](https://qiita.com/mczkzk/items/468ed4b36dbfa5121ca4)
- [Fetch - 現代の JavaScript チュートリアル](https://ja.javascript.info/fetch)
- [Promise の使い方、それに代わる async/await の使い方 \- Qiita](https://qiita.com/suin/items/97041d3e0691c12f4974#asyncとawait---非同期処理を同期処理っぽく書く)
- [Body \- Web API \| MDN](https://developer.mozilla.org/ja/docs/Web/API/Body)
- [ReadableStream \- Web API \| MDN](https://developer.mozilla.org/ja/docs/Web/API/ReadableStream)