JavaScriptの基礎 - Fetch API

提供: MochiuWiki : SUSE, EC, PCB

2026年2月21日 (土) 04:38時点におけるWiki (トーク | 投稿記録)による版 (ページの作成:「== 概要 == Fetch APIは、ネットワークリクエストを行うためのブラウザ標準のインターフェースである。<br> 従来の <code>XMLHttpRequest</code> と比較して、簡潔なAPIを提供しており、Promiseベースの設計により非同期処理の記述が容易になっている。<br> <br> <code>fetch()</code> 関数は、指定したURLへHTTPリクエストを送信してResponseオブジェクトに解決されるPromiseを…」)
(差分) ← 古い版 | 最新版 (差分) | 新しい版 → (差分)

概要

Fetch APIは、ネットワークリクエストを行うためのブラウザ標準のインターフェースである。
従来の XMLHttpRequest と比較して、簡潔なAPIを提供しており、Promiseベースの設計により非同期処理の記述が容易になっている。

fetch() 関数は、指定したURLへHTTPリクエストを送信してResponseオブジェクトに解決されるPromiseを返す。
GETリクエストによるデータ取得からPOSTリクエストによるデータ送信まで、あらゆるHTTP通信をサポートしている。

Fetch APIの使用時において、重要な特性として、HTTPエラー (404や500等) が発生してもPromiseはrejectされないことが挙げられる。
Promiseがrejectされるのはネットワークエラーが発生した場合のみであり、HTTPエラーの検出には response.ok プロパティを明示的にチェックする必要がある。

リクエストのキャンセルには AbortController を使用する。
AbortSignal.timeout() による簡易タイムアウト設定、AbortSignal.any() による複数シグナルの組み合わせも活用できる。

Reactの useEffect フック内でFetch APIを使用する場合、コンポーネントのアンマウント時にリクエストをキャンセルするクリーンアップ処理を実装することにより、
メモリリークおよびレースコンディションを防止できる。

Fetch APIは全ての最新のWebブラウザで対応しているが、Internet Explorerには対応していない。


fetch()の基本

基本構文

fetch() 関数の基本的な構文を以下に示す。

 fetch(url, options)


fetchメソッドの引数
引数 説明
url リクエスト先のURLを、文字列 または URLオブジェクトで指定する。
options オプションオブジェクト (省略可能)


下表に、options で指定可能なパラメータを示す。

optionsで指定可能なパラメータ
パラメータ 説明
method
  • HTTPメソッドを指定する。デフォルトは 'GET' である。
  • 指定可能な値: 'GET', 'POST', 'PUT', 'DELETE', 'PATCH'
headers
  • リクエストヘッダを指定する。
  • Headers オブジェクトまたはオブジェクトリテラルで指定できる。
body
  • リクエストボディを指定する。GET および HEAD リクエストには指定できない。
  • 文字列、Blob、FormData、URLSearchParams等を指定できる。
credentials
  • クッキーの送信方法を指定する。デフォルトは 'same-origin' である。
  • 指定可能な値: 'same-origin', 'include', 'omit'
mode
  • CORSモードを指定する。デフォルトは 'cors' である。
  • 指定可能な値: 'cors', 'no-cors', 'same-origin'
redirect
  • リダイレクトの処理方法を指定する。デフォルトは 'follow' である。
  • 指定可能な値: 'follow', 'error', 'manual'
signal リクエストをキャンセルするための AbortSignal オブジェクトを指定する。


Responseオブジェクト

fetch() が返すPromiseは、Response オブジェクトに解決される。
Response オブジェクトの主要なプロパティを以下に示す。

Responseオブジェクトのプロパティ
プロパティ 説明
ok ステータスコードが200〜299の範囲にあるかどうかを示すboolean値である。
HTTPエラーの検出に使用する。
status HTTPステータスコードを示す数値である。
(例: 200, 404, 500)
statusText ステータスに対応するテキストを示す文字列である。
(例: 'OK', 'Not Found')
headers レスポンスヘッダを含む Headers オブジェクトである。
url リダイレクト後の最終的なURLを示す文字列である。
redirected リダイレクトが発生したかどうかを示すboolean値である。


レスポンスボディの読み取り

Response オブジェクトは、ボディを読み取るための複数のメソッドを提供している。
いずれのメソッドもPromiseを返す。

レスポンスボディ読み取りメソッド
メソッド 戻り値 用途
response.json() JavaScriptオブジェクト JSONレスポンスの読み取り
response.text() 文字列 (UTF-8) テキストレスポンスの読み取り
response.blob() Blobオブジェクト 画像・ファイル等のバイナリデータの読み取り
response.arrayBuffer() ArrayBuffer 低レベルのバイナリデータの読み取り
response.formData() FormDataオブジェクト フォームデータの読み取り


※注意
レスポンスボディは1度だけ読み取り可能である。
同じレスポンスボディを複数回読み取る必要がある場合は、response.clone() でレスポンスを複製する。


GETリクエスト

基本的なGETリクエスト

基本的なGETリクエストの例を以下に示す。

 const response = await fetch('https://api.example.com/users')
 const data = await response.json()

 console.log(data)


fetch() はPromiseを返すため、await を使用することにより、Responseオブジェクトを取得できる。
続いて response.json()await することにより、JSONをJavaScriptオブジェクトに変換できる。

クエリパラメータの付与

クエリパラメータを付与する場合は、URLSearchParams を使用することにより、安全にエンコードできる。

 const params = new URLSearchParams({
    page: '1',
    limit: '10',
    search: 'javascript'
 })

 const response = await fetch(`https://api.example.com/users?${params}`)
 const data = await response.json()


URLSearchParams を使用することにより、特殊文字や日本語等を含む値も自動的にパーセントエンコードされる。
文字列連結によるURL構築は特殊文字を含む値でエラーが発生する可能性があるため、URLSearchParams の使用を推奨する。

ヘッダの設定

認証トークンや Accept ヘッダ等を設定する例を以下に示す。

 const response = await fetch('https://api.example.com/users', {
    headers: {
       'Authorization': 'Bearer token123',
       'Accept': 'application/json'
    }
 })

 const data = await response.json()


headers オプションにはオブジェクトリテラルを指定できる。
より複雑なヘッダ操作が必要な場合は、Headers オブジェクトを使用することも可能である。


POSTリクエスト

JSONデータの送信

JSONデータをPOSTリクエストで送信する例を以下に示す。

 const response = await fetch('https://api.example.com/users', {
    method: 'POST',
    headers: {
       'Content-Type': 'application/json'
    },
    body: JSON.stringify({ name: 'John', email: 'john@example.com' })
 })
 const data = await response.json()


JSONデータを送信する場合は、以下に示す事柄に注意する。

  • method'POST' を指定する。
  • headers'Content-Type': 'application/json' を設定する。
  • body には、JavaScriptオブジェクトを JSON.stringify() で文字列に変換したものを指定する。


FormDataの送信

ファイルアップロード等、マルチパートフォームデータを送信する例を以下に示す。

 const formData = new FormData()
 formData.append('name', 'John')
 formData.append('file', fileInput.files[0])
 
 const response = await fetch('https://api.example.com/upload', {
    method: 'POST',
    body: formData
    // Content-Typeは設定しない
    // Webブラウザが自動的に設定する
 })

 const data = await response.json()


FormData を送信する場合は、Content-Type ヘッダを手動で設定しない。
Webブラウザが自動的に multipart/form-data を設定して、適切なバウンダリ文字列も付加する。

手動で Content-Type を設定するとバウンダリが欠落し、サーバ側でのパースに失敗する。


エラーハンドリング

HTTPエラーの処理

Fetch APIの重要な特性として、HTTPエラー (404や500等) が発生してもPromiseはrejectされない。
この場合、Promiseは正常に解決されるが、response.okfalse になる。

response.ok を使用したHTTPエラーの処理例を以下に示す。

 const response = await fetch('https://api.example.com/users')
 if (!response.ok) {
    throw new Error(`HTTP error! status: ${response.status}`)
 }
 
 const data = await response.json()


response.ok はステータスコードが200〜299の場合に true となる。
HTTPエラーを適切に処理するには、このチェックを明示的に定義する必要がある。

Promiseがrejectされるのは、サーバに到達できない場合等のネットワークエラーが発生したときのみである。
この場合、TypeError がスローされる。

 try {
    const response = await fetch('https://api.example.com/users')
    const data = await response.json()
 }
 catch (error) {
    if (error instanceof TypeError) {
       console.error('ネットワークエラー:', error.message)
    }
 }


ネットワークエラーの原因には、インターネット接続の切断、DNSエラー、CORS違反等が含まれる。

包括的なエラーハンドリングパターン

HTTPエラーとネットワークエラーの両方を処理する包括的なパターンを以下に示す。

 async function fetchWithErrorHandling(url) {
    try {
       const response = await fetch(url)
       if (!response.ok) {
          throw new Error(`HTTP error! status: ${response.status}`)
       }
       return await response.json()
    }
    catch (error) {
       if (error instanceof TypeError) {
          console.error('ネットワークエラー:', error)
       }
       else if (error.name === 'AbortError') {
          console.error('リクエストがキャンセルされた')
       }
       else {
          console.error('エラー:', error)
       }
       throw error
    }
 }


このパターンでは、エラーの種類に応じて以下の分類で処理を行う。

エラーの種類と処理の分類
エラーの種類 説明
TypeError ネットワークエラー (接続失敗等)
error.name === 'AbortError' AbortController によるリクエストのキャンセル
それ以外 HTTPエラー等のその他のエラー



AbortControllerによるリクエストキャンセル

基本的な使い方

AbortController を使用することにより、進行中のリクエストをキャンセルできる。
キャンセル時には AbortError という名前の DOMException がスローされる。

 const controller = new AbortController()
 
 // 5秒後にキャンセルする
 setTimeout(() => controller.abort(), 5000)
 
 try {
    const response = await fetch('https://api.example.com/users', {
       signal: controller.signal
    })
    const data = await response.json()
 }
 catch (error) {
    if (error.name === 'AbortError') {
       console.log('リクエストがキャンセルされた')
    }
 }


AbortControllersignal プロパティを fetch()signal オプションに渡すことにより、
controller.abort() を呼び出したタイミングでリクエストがキャンセルされる。

Webブラウザの対応状況: Chrome 66以降、Firefox 57以降、Safari 11以降

AbortSignal.timeout

AbortSignal.timeout() は、指定したミリ秒後に自動的にタイムアウトするシグナルを生成するスタティックメソッドである。
AbortController を手動で作成する必要がなく、タイムアウト処理を簡潔に記述できる。

 const response = await fetch('https://api.example.com/users', {
    signal: AbortSignal.timeout(5000)
 })


タイムアウト発生時には AbortError ではなく、TimeoutError という名前の DOMException がスローされる。
これにより、ユーザによる明示的なキャンセル (AbortError) と タイムアウト (TimeoutError) を区別して処理できる。

Webブラウザの対応状況: Chrome 94以降、Firefox 103以降、Safari 16.4以降、Edge 94以降

AbortSignal.any

AbortSignal.any() は、複数のシグナルを受け取り、いずれかが中断されたときに中断する新しいシグナルを生成するスタティックメソッドである。
タイムアウトとユーザによるキャンセルを組み合わせる場合等に有用である。

 const controller = new AbortController()
 
 const response = await fetch('https://api.example.com/users', {
    signal: AbortSignal.any([
       AbortSignal.timeout(5000),
       controller.signal
    ])
 })


上記の例では、5秒のタイムアウトが発生 または controller.abort() が呼び出されるかのいずれか早い方でリクエストがキャンセルされる。

Webブラウザの対応状況: Chrome 123以降、Firefox 121以降、Safari 17.4以降、Edge 123以降


Reactでの使用パターン

useEffectでのデータ取得

Reactコンポーネントの useEffect フック内でFetch APIを使用する際の推奨パターンを以下に示す。

 import { useState, useEffect } from 'react'
 
 function UserList() {
    const [data, setData] = useState(null)
    const [loading, setLoading] = useState(true)
    const [error, setError] = useState(null)
 
    useEffect(() => {
       const controller = new AbortController()
 
       const fetchData = async () => {
          try {
             setLoading(true)
             const response = await fetch('https://api.example.com/users', {
                signal: controller.signal
             })
             if (!response.ok) throw new Error(`HTTP ${response.status}`)
             const result = await response.json()
             setData(result)
             setError(null)
          }
          catch (error) {
             if (error.name !== 'AbortError') {
                setError(error.message)
             }
          }
          finally {
             setLoading(false)
          }
       }
 
       fetchData()
 
       return () => controller.abort()
    }, [])
 
    if (loading) return <div>読み込み中...</div>
    if (error) return <div>エラー: {error}</div>
    return <pre>{JSON.stringify(data, null, 2)}</pre>
 }


useEffect のクリーンアップ関数で controller.abort() を呼び出すことには以下の重要な意義がある。

  • メモリリークの防止
    コンポーネントがアンマウントされた後に非同期処理が完了しても、状態の更新が実行されなくなる。
  • 不要なリクエストのキャンセル
    コンポーネントがアンマウントされた時点で進行中のリクエストをキャンセルする。
  • レースコンディションの防止
    依存配列の値が変化して再レンダリングが発生した場合、前のリクエストをキャンセルしてから新しいリクエストを開始する。


AbortError はコンポーネントの正常なライフサイクルの一部であるため、エラーとして扱わないよう error.name !== 'AbortError' でフィルタリングしている。


関連情報