【JavaScript】fetch関数って何？

# `fetch`とは
[`fetch`](https://developer.mozilla.org/ja/docs/Web/API/Window/fetch)とは、簡単に言うと**HTTPリクエストを送る**関数です。
引数にリクエストを送るURLを入れると、リクエストが送信できます。

```js
fetch('https://example.com')
```

`fetch`関数はプロミスを返します。
そのため`await`と組み合わせて使い、リクエストが完了するまで待機することが多いです。

```js
const response = await fetch('https://example.com')
```

なお、このプロミスは、リクエストが正常に完了したら[`Response`](#response)オブジェクトで履行されます。
そのためこのコードでの`response`には`Response`オブジェクトが入ります。

## 例
`Response`オブジェクトはいくつかのメソッドとプロパティを持っています。
詳細は[`Response`](#response)で解説しますが、ここでは`text`の使用例を紹介します。

以下は`text`メソッドを使ってレスポンスボディを文字列にし、それをログに出力する例です。

```js
const response = await fetch('https://example.com')
const text = await response.text()

console.log(text) // レスポンスボディを文字列にしたものが出力される
```

:::note warn
ブラウザから実行した場合、リクエストがCORSポリシーに違反しているという理由でリクエストが拒否されることがあります。

その場合はNode.jsなどのサーバーサイドから実行するか、[https://example.com](https://example.com)のコンソールから実行してみてください。
:::

# `Request`
`Request`オブジェクトは**HTTPリクエスト**を示すオブジェクトです。
これは`fetch`の引数に入れたりできます。

```js
const request = new Request('https://example.com')
const response = await fetch(request)
```

`Request`は第一引数にURL[^1]を、第二引数にオプションを取ります。

```js
new Request('ここにURL', {
  // ここにオプション
})
```

:::note
`fetch`の引数は`Request`のコンストラクタの引数と同じです。
そのため`Request`で紹介する内容はそのまま`fetch`の引数に流用できます。
:::

## `options`

第二引数の`options`はオブジェクトになっていて、いくつかのオプションを入れることができます。
全てのオプションは[こちら](https://developer.mozilla.org/ja/docs/Web/API/RequestInit)から見れます。

ここでは、よく使いそうなオプションを挙げておきます。
- `method`: [リクエストメソッド](https://developer.mozilla.org/ja/docs/Web/HTTP/Methods)、`GET`や`POST`など
- [`headers`](#headers): リクエストヘッダ
- [`body`](#body): リクエストボディ、JSON形式の文字列など
- [`signal`](#signal): リクエストを中断するのに使える

### `headers`
リクエストヘッダを示すオブジェクトが入ります。
これはヘッダー名がキー、ヘッダーの値が値という形式です。

よく使うヘッダーの一つに、`'Content-Type': 'application/json'`があります。
これは例えば、`POST`リクエストのボディがJSON形式であることを示すのに使います。

```js
await fetch('https://example.com', {
  method: 'POST',
  // ヘッダーを指定
  headers: {
    // bodyがJSON形式であることを示す
    'Content-Type': 'application/json'
  },
  // リクエストボディを入れる
  body: JSON.stringify({ message: 'Hello World!' })
})
```

また、ここには[`Header`](https://developer.mozilla.org/ja/docs/Web/API/Headers)オブジェクトを入れることもできます。

### `body`
リクエストボディを入れられるオプションです。
これは以下の形式に対応しています。

- 文字列（`string`）
- `Blob`
- `FormData`
- etc...

例えば、先ほどの`Content-Type`ヘッダーや`JSON.stringify`と組み合わせて、JSON形式のリクエストを送ることができます。

```js
// リクエストボディに入れるオブジェクト
const bodyObj = {
  username: 'nanasi',
  post: {
    title: 'Hello World!',
    body: 'ここに本文'
  }
}

await fetch('https://example.com', {
  method: 'POST',
  headers: {
    // bodyがJSON形式であることを示す
    'Content-Type': 'application/json'
  },
  body: JSON.stringify(bodyObj) // JSON形式の文字列に変換する
})
```

:::note warn
`GET`や`HEAD`など、一部のリクエストメソッドでは`body`オプションを指定できません。
:::

### `signal`
リクエストを途中で中断させるのに使えます。

HTTPリクエストは実行にそれなりの時間やコストがかかることも多いです。
そのため途中で**不要になったリクエストを中断**し、リソースの節約を試みることができます。

これには[`AbortController`](https://developer.mozilla.org/ja/docs/Web/API/AbortController)の`signal`を使うことが多いと思います。

```js
// コントローラーを作成
const contorller = new AbortController()

// 通常のリクエスト
const promise = fetch('https://exampel.com', {
  signal: controller.signal // シグナルを設定
})

// リクエストを中断する
controller.abort()

// fetchのプロミスがAbortErrorで拒否される
```

詳細は[こちらの記事](https://idealump.com/service/lab/416)が参考になると思います。

# [`Response`](https://developer.mozilla.org/ja/docs/Web/API/Response)
リクエストの**レスポンス**を示すオブジェクトです。

これはコンストラクタから生成するというより、`fetch`などのAPIから返されるものを使うことが多いと思います。

```js
// Responseオブジェクトが入る
const response = await fetch('https://example.com')

response.ok // true
await response.text() // レスポンスボディの文字列
```

## メソッド / プロパティ

### `ok`
リクエストが成功したかを示すプロパティで、値は`boolean`です。

```js
const response = await fetch('https://example.com')
response.ok // true
```

これはステータスコード（[`status`](#status)）が`200` ~ `299`なら`true`、それ以外（`404`や`500`）なら`false`になります。

```js
// 存在しないページにリクエストを送る
const response = await fetch('https://example.com/not-found')
response.ok // false
```

### `status`
リクエストの[HTTPステータスコード](https://developer.mozilla.org/ja/docs/Web/HTTP/Status)を示すプロパティです。
ステータスコードには例えば`200`（正常に完了）や`404`（ページが見つからない）、`500`（サーバーエラー）などが挙げられます。

```js
// 存在するページにリクエストを送る
const response = await fetch('https://example.com')
response.ok // true
response.status // 200

// 存在しないページにリクエストを送る
const response = await fetch('https://example.com/not-found')
response.ok // false
response.status // 404
```

ステータスコード一覧は[このサイト](https://www.itmanage.co.jp/column/http-www-request-response-statuscode/#anc003)がわかりやすかったです。

### `text`
レスポンスボディを**文字列**にして返すメソッドです。

このメソッドは文字列で履行されるプロミスを返します。
そのため、`then`や`await`とセットで使うことが多いです。

```js
const response = await fetch('https://example.com')

// リクエストが正常に完了したかチェック
if (!response.ok) {
  throw new Error(`リクエストが正常に完了しませんでした`)
}

// レスポンスボディを取得
const body = await response.text()
console.log(body) // レスポンスボディが出力される
```

なお、`then`を使って`response`変数を省略することもできます。

```js
const body = await fetch('https://example.com')
  .then(res => res.text()) // thenを使って一気に処理する
console.log(body) // レスポンスボディが出力される
```

`response.ok`などでのチェックをすっ飛ばして直接レスポンスボディを使いたいときは、この方法を使うと簡潔に書けます。

### `json`
レスポンスボディがJSON形式の場合、`json`メソッドを使うことでJavaScriptのオブジェクトとしてボディを取得することができます。

```js
const body = await fetch('https://example.com') // 何かJSONを返すAPI
  .then(res => res.json()) // jsonメソッドを使用
console.log(body) // { なんらかのオブジェクト }
```

これは`res.text`で取得したテキストを`JSON.parse`を使ってオブジェクトにした場合と同じように動きます。

```js
// JSON.parseを使う場合
const body = await fetch('https://example.com')
  .then(res => res.text())
  .then(text => JSON.parse(text))

// textを使う場合
const body = await fetch('https://example.com')
  .then(res => res.json())
```

なお、レスポンスボディがJSONでなかった場合、このメソッドは`SyntaxError`を投げます。
レスポンスの形式には注意してください。

[^1]: 第一引数に`Request`を入れることで、`Request`オブジェクトを複製することもできます。詳細は[こちら](https://developer.mozilla.org/ja/docs/Web/API/Request/Request)