【JavaScript】fetchとは?非同期通信の基礎と使い方をわかりやすく解説

はじめに

JavaScriptで非同期通信を行う際に多くの方が利用するのがfetchです。
以前はXMLHttpRequestという機能を使って非同期リクエストを送る方法が一般的でしたが、コードが複雑になりがちという声があったようです。
そこで、よりシンプルに書ける手段としてfetchが登場しました。
このfetchはシンプルな構文でリクエストやレスポンスを扱いやすく、初めてでも学びやすい特徴を持っています。
とはいえ、初学者の方にとっては「そもそも非同期通信って何?」というところから整理が必要ではないでしょうか。

このように、Web開発ではサーバーとやりとりしながら動的にデータを更新することが当たり前になっています。
fetchを理解することは、ブラウザ上のJavaScriptで効率的にデータを取り扱うための最初の一歩です。
ここから一緒にfetchの概念や使い方を整理していきますので、ぜひ最後まで読んでみてください。

この記事を読むとわかること

  • fetchとは何か
  • 基本的なGETリクエストやPOSTリクエストの書き方
  • 非同期処理をスムーズに行うためのasync/await構文の活用方法
  • 実務でも役立つエラーハンドリングやCORSの基本
  • 応用的な使い方やベストプラクティス

JavaScript fetchとは

fetchは、JavaScript標準に用意されている関数のひとつです。
サーバーからデータを取得したり、逆にサーバーにデータを送りたい場合に利用します。
これがなぜ重要かというと、Webアプリケーションでは動的にページを書き換えたり、ユーザー入力をサーバーに送信して処理結果を受け取ることが日常的に行われるからです。

たとえば、SNSで投稿を一覧表示したり、検索フォームから入力したキーワードに対して結果を表示したりする場面を想像してみてください。
これらはすべて、非同期的にサーバーとやりとりして更新する仕組みになっています。
fetchを使えば、比較的読みやすいコードでそれらの機能を実装できます。

非同期通信の概要

非同期通信とは、ブラウザで処理を進めながら、必要なタイミングでサーバーにリクエストを送り、レスポンスを受け取る仕組みを指します。
同期通信と異なり、いちいち画面全体をリロードしなくても、必要なデータだけをサーバーから取得して部分的にページを更新できます。
この仕組みにより、画面が途切れずにサクサク動作する感覚をユーザーに提供しやすくなるわけです。

多くのWebサイトやWebアプリケーションが、この非同期通信を活用しています。
従来はXMLHttpRequestで実装していましたが、コード量が多くなることがありました。
fetchはPromiseベースで実装されており、より直感的に書ける点が選ばれる理由になっています。

Promiseとfetchの関係

fetchは非同期通信を行う際に、Promiseというオブジェクトを返します。
Promiseとは「処理の成功・失敗をひとまとめに扱える仕組み」を提供するもので、リクエストが成功したかどうかをthenやcatchなどのメソッドで追跡できるようになります。
これによって、コールバック地獄と呼ばれるようなネストが深い書き方を避けられるのです。

たとえば、サーバーにリクエストを送ったら、すぐには結果が返ってきません。
その間にもほかの処理は進めたい場合、Promiseを使うとコード全体の流れがわかりやすくなります。
fetchが返すPromiseは成功するとレスポンスオブジェクトが得られ、失敗するとエラーのハンドリングが可能です。

基本的な使い方

fetchの使い方を簡単なコード例で見てみましょう。
まずはサーバーからデータを取得する、いわゆるGETリクエストから始めます。
JavaScriptで何かAPIを叩く場合は、ほぼこの書き方を発展させた形になっていくと考えてください。

GETリクエストの例

fetch("https://example.com/api/data")
  .then(response => {
    if (!response.ok) {
      throw new Error("サーバーエラーが発生しました");
    }
    return response.json();
  })
  .then(data => {
    console.log("取得したデータ:", data);
    // ここで画面表示を更新したりする
  })
  .catch(error => {
    console.error("エラー:", error);
  });

上のコード例では、fetch("URL")でAPIにリクエストを送っています。
thenでレスポンスオブジェクトを受け取り、response.okがtrueかどうかでエラーを判定します。
その後、response.json()でJSON形式のデータを扱いやすい形に変換しています。

たとえば、取得したデータをコンソールログで確認できるので、開発中は動作を確認しながら進めることができます。
catchの部分ではネットワークエラーやサーバーエラーなどをまとめて捕捉する構造になっています。

POSTリクエストの例

次に、サーバーにデータを送るPOSTリクエストの例を見てみましょう。
このようにオプションを指定してメソッドやボディを設定します。

fetch("https://example.com/api/submit", {
  method: "POST",
  headers: {
    "Content-Type": "application/json"
  },
  body: JSON.stringify({
    username: "user01",
    comment: "こんにちは"
  })
})
  .then(response => {
    if (!response.ok) {
      throw new Error("送信に失敗しました");
    }
    return response.json();
  })
  .then(result => {
    console.log("サーバーからのレスポンス:", result);
  })
  .catch(error => {
    console.error("エラー:", error);
  });

methodを"POST"にしている点や、headersでContent-Typeを指定している点に注目してください。
また、bodyにはJavaScriptオブジェクトをJSON文字列に変換したものを入れています。
こうすることで、API側がJSON形式のデータとして受け取れるようになります。

POSTリクエストは、ユーザーがフォームで入力した内容をサーバーに送って処理する場合や、新規データを追加する仕組みなどでよく利用されます。
実務ではサーバー側でのバリデーションや、DBへの書き込みロジックと組み合わせて使われます。

使いどころと活用シーン

fetchを使うことで、単純に「データを読み込む」「データを送る」という以外にも、さまざまな使い方が可能になります。
実務では以下のようなシーンでfetchが活躍することが多いでしょう。

Webアプリ開発におけるデータ取得

たとえば、掲示板やチャットアプリなど、ユーザーが投稿したデータを都度更新して表示するケースを考えてみてください。
画面を完全にリロードすることなく、新しい投稿だけをfetchで取り込み、コメント欄に表示します。
また、eコマースサイトでは商品の在庫情報や価格をリアルタイムに取得して動的に表示できます。

このような場面で、可読性の高いコードを書くことは開発効率に直結します。
fetchならPromiseやasync/await構文を使ってすっきり書けるので、あとからコードを見返しても理解しやすいでしょう。

サードパーティAPIを利用するケース

第三者が提供するAPIを利用するときにもfetchはよく使われます。
たとえば地図情報サービスのAPIを叩き、地名を入力したら地図上でピンを立てるようなWebアプリを作る場合です。
fetchで外部のAPIにアクセスし、得られたデータを加工してページに反映することで、さまざまな機能を素早く実装できます。

これによって、たとえばSNSのシェアカウントを取得したり、為替レートをリアルタイムで取得したりすることも簡単です。
ただし、APIごとに利用条件や認証方法が異なる場合があるため、そのあたりは事前に確認しておきましょう。

fetchとXHRの違い

fetchはXMLHttpRequest(XHR)をより使いやすくしたものと言っても差し支えありません。
ここでは、両者の違いをざっくり整理しておきます。
実際のコードを比較するとfetchのほうがシンプルに書けるため、初心者の方は特にfetchを優先して覚えるとよいかもしれません。

可読性と保守性

XHRを使うときはnew XMLHttpRequest()とインスタンスを生成し、メソッドを呼び出して送信し、イベントリスナーで結果を受け取る流れになります。
そのため、処理が増えるほどイベントリスナーが増えたり、コールバックが入れ子になったりして可読性が下がりがちです。
一方でfetchはPromiseベースなので、thenやcatchをつなげて書く、またはasync/awaitを使うなど、コード量が抑えられて直感的です。

また、fetchは関数呼び出し自体がわかりやすい点も魅力です。
ネットワーク通信というやや複雑な動きを、単純な関数を呼ぶ感覚で扱えるようになりました。
可読性が高いほどバグを見つけやすく、修正しやすいというメリットにつながります。

レスポンスデータの扱いやすさ

XHRではレスポンスの受け取り方が状況によって変わり、受信後に改めてJSONパースをしなければならないことがあります。
fetchの場合はresponse.json()という専用のメソッドが提供されており、簡単にJavaScriptのオブジェクトへ変換が可能です。
この点は開発のスピードアップに寄与します。

さらにはレスポンスのステータスやヘッダー情報などもわかりやすく取得できるため、成功か失敗かの判定処理も書きやすくなっています。
こうした点から、XHRと比べてコードの扱いやすさがfetchの利点といえるでしょう。

fetchのオプション設定

fetchでは、第二引数としてオプションオブジェクトを渡すことで、いろいろな挙動をカスタマイズできます。
たとえば、メソッドの指定やヘッダーの追加、ボディの内容などです。
こうしたオプション設定を使いこなせば、GETやPOSTにとどまらず、PUTやDELETEなどHTTPのさまざまなメソッドを活用できます。

リクエストヘッダーの追加

Webアプリ開発では、APIサーバーに特別なヘッダーを送る必要があるケースがあります。
例えば、APIキーや言語設定など、認証や設定を伝えるための情報です。
以下のようにfetchの第二引数のheadersにオブジェクトとして追加します。

fetch("https://example.com/api/items", {
  method: "GET",
  headers: {
    "Authorization": "Bearer 1234567890",
    "Accept-Language": "ja"
  }
})
  .then(response => response.json())
  .then(data => console.log(data))
  .catch(err => console.error("エラー:", err));

このように指定すると、APIサーバー側は適切な認証や言語設定を判別してレスポンスを返してくれます。
アプリの仕様次第で、かなりいろいろなヘッダーを利用することがあるため、覚えておくと便利です。

メソッドやボディ設定

先ほどのPOSTリクエスト例でも触れましたが、methodを切り替えればPUTやDELETEリクエストを送ることも可能です。
また、bodyにはJSONのほか、フォームデータやテキストなどを自由に詰めて送信できます。
APIの仕様に合わせて、指定の形式でデータを送るよう実装することが多いでしょう。

なお、Content-Typeヘッダーを合わせて指定しないと、サーバー側でデータを正しく解釈できないケースがあります。
JSONを送る場合はapplication/json、フォームをそのまま送る場合はmultipart/form-dataなど、要件に応じて選んでください。

非同期制御のポイント

fetchが返すPromiseを使いこなすためには、JavaScriptのPromise概念に理解があるとスムーズです。
ここでは、then構文とasync/await構文という2つの書き方を簡単に比較してみましょう。
書き方が違うだけで、やっていることは基本的に同じです。

then構文

すでにコード例で登場したように、fetch("URL").then(...).catch(...)という流れです。
thenは「処理が成功したとき、次に実行すること」、catchは「処理が失敗したときに実行すること」を示します。
流れが明確なので初心者にもわかりやすい反面、thenを多用するとコードのネストが深くなることがあります。

たとえば、複数のリクエストを順番に行い、その結果ごとに追加の処理を行う場合、thenのチェーンが長くなるかもしれません。
そんなときは次に紹介するasync/awaitが便利です。

async/awaitの活用

async/awaitを使うと、あたかも同期的に処理が進んでいるかのような書き方ができます。
そのため、複数のAPIを順に叩いて結果を合成するようなシナリオでも、可読性を高く保つことができます。

async function fetchData() {
  try {
    const response = await fetch("https://example.com/api/info");
    if (!response.ok) {
      throw new Error("レスポンスにエラーがありました");
    }
    const data = await response.json();
    console.log("取得したデータ:", data);
  } catch (error) {
    console.error("エラー:", error);
  }
}

fetchData();

awaitをつけると、そのPromiseが解決(成功もしくは失敗)するまで待機してから次に進みます。
これにより、thenを並べる書き方よりも見通しが良くなります。
ただし、awaitを使うには関数をasync functionとして宣言する点を忘れずに。

エラーハンドリング

非同期通信は常に成功するわけではありません。
ネットワークが切断されていたり、サーバーがエラーを返したり、さまざまな失敗ケースを想定する必要があります。
fetchを使う場合でも、エラーが起きたときにどう対応するかを明確にしておくと実務で役立つでしょう。

ネットワークエラーへの対処

最も基本的な対処法は、Promiseのcatchを利用することです。
fetchが失敗した場合、つまりネットワークエラーやドメインが存在しないときなどは、catchに処理が飛んできます。
「タイムアウトになったらユーザーに通知を出す」「オフライン時はキャッシュから代わりのデータを表示する」などの実装が考えられます。

また、JavaScriptのブラウザ環境によってはオフライン状態を検知するAPIなども存在します。
あわせて活用すると、ユーザーの操作を止めることなくスムーズにエラーをフォローできます。

ステータスコード別の対処

レスポンスのステータスコードによっては、400番台や500番台のエラーが起こる場合があります。
fetch自体は成功扱いでも、response.okがfalseになる可能性があるので要注意です。
たとえば404エラー(存在しないリソースへのアクセス)や500エラー(サーバー内部エラー)などをキャッチしたい場合は、response.okをチェックしましょう。

const response = await fetch("https://example.com/api/detail");
if (!response.ok) {
  if (response.status === 404) {
    console.error("リソースが見つかりませんでした");
  } else {
    console.error("サーバーエラー:", response.status);
  }
  return;
}

このように書いておくと、エラー内容がなぜ発生したかをユーザーに案内しやすくなります。
アプリの要件によっては、ユーザーに分かりやすいエラーメッセージを表示する仕組みを作ることが重要になるでしょう。

JSON以外の形式への対応

fetchはJSONに限らず、さまざまな形式のデータを取り扱えます。
テキストや画像ファイル、バイナリデータなどにも対応できますので、基本的な書き方を把握しておくと便利です。

テキスト形式のデータ取得

JSONではなくテキストとして取得したい場合は、response.text()を使用します。
これは単純な文字列をやりとりするAPIや、簡易的なログ出力を取り込みたい場合などに役立ちます。
以下の例では、取得したテキストをそのまま表示に使うケースを想定してみます。

fetch("https://example.com/api/message")
  .then(response => response.text())
  .then(text => {
    console.log("取得したテキスト:", text);
  })
  .catch(err => console.error("エラー:", err));

また、テキストを一時的に加工してページに埋め込むような処理をすることもあるかもしれません。
この場合でもJSONと同様にPromiseを返す構造なので、同じ流れでハンドリングできます。

BlobやArrayBufferへの対応

画像や音声、動画などのバイナリデータを取り扱いたい場合はresponse.blob()response.arrayBuffer()を利用します。
たとえば、ユーザーがアップロードした画像ファイルを別のサーバーに中継したい場合や、ダウンロード用のファイルを動的に取得してリンクを生成する場合などが考えられます。

画像をプレビュー表示したい場合は、取得したBlobをURL.createObjectURLで生成したURLに紐付けるといった方法がよく用いられます。
このように、fetchを使えば多種多様なデータ形式にアクセスできるので、フロントエンド開発の幅が広がるはずです。

認証やセキュリティ面

実際のWeb開発では、APIキーや認証トークンをヘッダーに載せて送るケースが非常に多いです。
ここではfetchを使った認証の概念をざっくり紹介します。
セキュリティ面は特に注意が必要であり、情報漏えいを防ぐためにも実装方法を慎重に検討しましょう。

APIキーの送信

APIキーを利用するサービスにアクセスする場合は、決められたヘッダーにAPIキーを設定することが一般的です。
先ほどの例でも登場しましたが、Authorizationx-api-keyなどのヘッダーを使ってキーを送信します。
サーバー側は受け取ったキーが正しいかどうかを検証し、正しいならばレスポンスを返す流れです。

APIキーは第三者に盗まれると、APIへの不正アクセスに使われる可能性があります。
そのため、フロントエンドにキーを直書きするかどうかはプロジェクトの規模や性質を考慮したうえで決定する必要があるかもしれません。

認証トークンを使う場合

たとえばログイン後にトークンを受け取り、それをヘッダーに付与して以降のリクエストを送るようなフローもよく見かけます。
トークンが有効であればユーザーの操作を認める、といった仕組みです。
fetchを使う場合でも、オプションのheadersにトークンを追加するだけなので、実装そのものは特に難しくありません。

問題は、セキュアな取り扱い方をどう設計するかです。
セッションストレージにトークンを保存するのか、HTTPOnlyのクッキーを使うのかなど、要件に合わせて検討しましょう。
これらはクライアントサイドJavaScriptだけでなく、サーバー側との連携が前提となる話題でもあります。

fetchでできる応用的な使い方

ここまで紹介した基本的なfetchの書き方だけでも多くの場面で活躍します。
しかし実際には、複数リクエストを並行して投げたり、ストリーミングを扱ったりといった高度な使い方をすることがあります。
今回はイメージを掴むために、少しだけ概要をご紹介しましょう。

並列リクエスト処理

複数のAPIから同時にデータを取得したい場合、Promiseを並列で処理するPromise.allが便利です。
fetchはPromiseを返すため、複数のfetch呼び出しを一度に実行して結果を合体させることができます。
たとえば、ユーザー情報と関連する商品情報を同時に読み込みたいようなケースで、処理時間を短縮できる可能性があります。

const fetchUser = fetch("https://example.com/api/user").then(res => res.json());
const fetchItems = fetch("https://example.com/api/items").then(res => res.json());

Promise.all([fetchUser, fetchItems])
  .then(([user, items]) => {
    console.log("ユーザー:", user);
    console.log("アイテム:", items);
  })
  .catch(err => console.error("エラー:", err));

このように書くと、両方のfetchが完了した後に次の処理に移るため、効率的な通信が可能です。
async/awaitで書く場合も同じ考え方で進められます。

ストリーミングへの対応

動画配信や音声ストリーミングなど、大容量データを逐次受け取るようなケースでは、fetchのストリーム機能が使えることがあります。
これはレスポンスを細切れに受け取りながら処理を進める仕組みです。
たとえばチャンク単位で取得して再生を始める、といった動きを想定した実装が可能です。

ただし、こうした高度な機能は使用できる環境が限られる場合があるため、実際に利用する際はブラウザの対応状況を確認することが必要かもしれません。
また、Node.js環境で実行する場合にはNode.js特有のストリームAPIとの整合性を考慮することもあります。

CORSへの対応

fetchを使う上でしばしば話題になるのがCORS(Cross-Origin Resource Sharing)です。
これは、異なるドメインやポートへのリクエストをブラウザが制限する仕組みで、セキュリティ上の理由から導入されています。
理解しておかないと、別ドメインのAPIを叩いたときにエラーが起こるかもしれません。

同一オリジンポリシー

ブラウザは、同一オリジン(ドメインやポート、プロトコルが同じ)でないとリソースを取得する際に制限をかけます。
たとえば自サイトがhttps://mysite.comで、APIサーバーがhttps://api.othersite.comの場合、別オリジンのリソースを取得しようとしている扱いになるわけです。
このままだとリクエストそのものは送れても、レスポンスをブラウザがブロックしてしまう場合があります。

アクセス制御ヘッダーの基本

CORSを回避するためには、サーバー側が適切なレスポンスヘッダーを設定する必要があります。
代表的なのはAccess-Control-Allow-Originで、ここにクライアント側のドメインを指定すると、ブラウザがレスポンスを許可してくれるようになります。
つまり、サーバー側の設定次第でCORSエラーを回避できるという仕組みです。

フロントエンドからfetchでアクセスするだけではなく、サーバーもCORSに対応しているかどうかが重要になります。
自分でコントロール可能なサーバーなら設定を変更すればよいですが、外部サービスのAPIを利用する場合は、そのサービス側がCORSに対応しているかどうか事前に確認が必要です。

フォーム送信とfetch

ユーザーがフォームに入力した情報をサーバーに送る場合にもfetchは活用できます。
ここでは、フォームデータをまとめてサーバーにPOSTしたいケースを考えてみましょう。

フォームデータの送信

フォームからの入力は、FormDataオブジェクトを使って取得することができます。
これをfetchのbodyとして送ることで、伝統的なフォーム送信と同じような仕組みを実装できます。
ただし、HTMLの<form>要素をそのまま使うとページリロードが起きるので、JavaScriptで制御する点に注意が必要です。

const formElement = document.querySelector("#myForm");
formElement.addEventListener("submit", async (event) => {
  event.preventDefault();
  
  const formData = new FormData(formElement);
  try {
    const response = await fetch("/api/form", {
      method: "POST",
      body: formData
    });
    const result = await response.text();
    console.log("フォーム送信結果:", result);
  } catch (error) {
    console.error("エラー:", error);
  }
});

FormDataを使うと、Content-Typeをマルチパート形式に自動的に設定して送ってくれます。
サーバー側では通常のフォーム送信と同様にデータを受け取れるため、既存の仕組みに組み合わせやすいのも利点です。

画像やファイルの送信

フォームにファイル入力欄がある場合、画像やPDFなどのバイナリファイルも同様にFormDataに含めて送ることができます。
特別なコードを組まなくても、ファイルオブジェクトがまとめて送信されるので便利です。
サーバー側はアップロードされたファイルを受け取り、必要に応じてファイルシステムやクラウドストレージに保存する流れを組めばよいでしょう。

ただし、ファイルサイズが大きい場合や多数のファイルを扱う場合は、アプリのパフォーマンスを検討しておく必要があります。
通信量が増えすぎると、ユーザー体験に影響を与えるかもしれません。

fetchのデバッグ方法

fetchでネットワーク通信を行う際、デバッグにはブラウザの開発者ツールが非常に役に立ちます。
ネットワークタブやコンソールログを活用し、リクエストとレスポンスの実体を確認すると不具合の原因を探しやすいでしょう。

ブラウザの開発者ツール

ChromeやFirefoxなどの開発者ツールを開くと、Networkタブで発生しているリクエストの一覧が見られます。
HTTPステータスコード、レスポンスヘッダー、送られてきたデータの詳細などが確認できます。
特にステータスコードやレスポンスヘッダーに異常がないかをチェックすると、CORSエラーや認証エラーなどを早期に発見できます。

また、レスポンスボディに何かしらのエラーメッセージが含まれている場合もあるので、一度テキストとして開いてみることをおすすめします。
fetchでエラーが起きたときは、コンソールにもメッセージを出力しておくとスムーズに原因を特定できます。

レスポンス内容の確認

コード上でもレスポンス内容をデバッグしやすいようにしておくと、問題解決が早まるはずです。
たとえば、ステータスコードやヘッダー情報をログに出力し、JSONの中身をコンソールに表示するなどです。
これにより、サーバーが想定外のフォーマットでデータを返していないかなどを把握できます。

fetchでレスポンスを受け取る際に、どのメソッドで変換するか(json、text、blobなど)を間違えていないかも要チェックです。
正しいメソッドを選んだはずが間違えていると、受け取るデータの型と中身が食い違ってエラーになることがあります。

fetchとリトライ戦略

ネットワークが不安定な環境で、たまに失敗するかもしれないリクエストを行う場合、リトライの仕組みを組み込むことがあります。
fetch自体にはリトライ機能は備わっていないため、自分で実装する必要があります。

リトライの仕組み

一度fetchが失敗したら、数秒後に再度fetchを呼び出し、一定回数までは再チャレンジするように組む方法です。
ユーザーに特別な操作をさせなくても自動的に再試行して、成功したら処理を続行できるようにします。
ただし、同じリクエストを繰り返し送るので、サーバーへの負荷や意図しない重複処理に注意が必要です。

リトライの回数や間隔をどう設定するかは、通信環境やAPIの特性によります。
あまり回数が多すぎると、障害発生時にサーバーを圧迫したり、無意味に待たされる時間が増えたりするリスクもあるでしょう。

再帰的にfetchする例

簡単な再帰実装の例を示してみます。
あくまで概念的なサンプルなので、実際はタイマーを挟んだり、fetchのステータスを見てハンドリングを細分化するとより現実的になります。

async function fetchWithRetry(url, options = {}, retryCount = 3) {
  try {
    const response = await fetch(url, options);
    if (!response.ok) {
      throw new Error("ステータス異常: " + response.status);
    }
    return response.json();
  } catch (error) {
    if (retryCount <= 0) {
      throw error;
    }
    console.warn(`リトライ残り回数: ${retryCount}. エラー:`, error);
    return fetchWithRetry(url, options, retryCount - 1);
  }
}

fetchWithRetry("https://example.com/api/data", {}, 3)
  .then(data => console.log("成功:", data))
  .catch(err => console.error("最終的に失敗:", err));

この例では、fetchが失敗したり、response.okがfalseだった場合に例外を投げてキャッチし、リトライ回数を減らして再度同じfetchを試す構造になっています。
実用するなら、タイムアウト処理やステータスコードの判定なども調整する必要がありますが、リトライ戦略の基本イメージとしてはこのように書くことができます。

実務でのベストプラクティス

fetchは手軽に使える反面、きちんと設計しないと大規模なコードベースで管理が煩雑になる可能性があります。
ここでは実務で役に立つポイントをいくつかピックアップします。

コードの分割と再利用

APIコールが増えてくると、あちこちでfetchが呼び出され、同じようなパラメータやエラーハンドリングが散乱することがあります。
そこで、よく使うAPIに対しては専用の関数を作り、まとめておくとコードを整理しやすくなります。
APIのURLや基本的なオプションを関数内部にまとめておけば、呼び出し側はシンプルに書けるでしょう。

たとえば、ユーザー情報を取得するための関数getUserInfo()、コメントを投稿するための関数postComment()など、機能ごとに整理しておくと保守性が高まります。
チーム開発では特に、API仕様変更に対応するときなどに効果を発揮します。

fetchラッパ関数の設計

共通処理をまとめたい場合、fetchをラップする関数を用意する方法もあります。
共通のヘッダー設定、認証トークンの付与、エラーの標準化などをまとめて実装する仕組みを作るイメージです。
たとえば、以下のように共通関数customFetchを作って使い回すことが可能です。

async function customFetch(url, options = {}) {
  const defaultHeaders = {
    "Content-Type": "application/json"
  };
  const mergedOptions = {
    ...options,
    headers: {
      ...defaultHeaders,
      ...options.headers
    }
  };
  const response = await fetch(url, mergedOptions);
  if (!response.ok) {
    throw new Error(`ステータスコード: ${response.status}`);
  }
  return response.json();
}

このような作りにすると、アプリ全体で一定のルールに従った通信処理を行いやすくなり、保守性が向上します。
あまり複雑に作りすぎない範囲で、よく使う処理をうまくまとめておくと便利です。

fetchを扱うときは、Promiseやエラーに関するJavaScriptの基本をしっかり理解しているほどスムーズに進めやすいです。

また、エラー処理を含めたログ出力なども、ここで一括で扱えるため、トラブルシュートしやすいメリットがあります。
チーム全員が同じ書式でコードを書く環境が整うと、開発効率が高まる傾向があるかもしれません。

まとめ

ここまで、JavaScript fetchの概要から基本的な使い方、そして実務で役立つ応用的な話題まで幅広く解説してきました。
非同期通信はWebアプリ開発における重要な要素であり、fetchがその中心的な存在として活躍します。
Promiseベースで直感的に書けること、JSONの変換が簡単にできること、オプション設定が豊富であることなどがfetchの魅力です。

実務においては複数のAPIコールを効率的にまとめたり、認証トークンを自動的に付与したり、CORSの設定を考慮したりといった点をしっかり押さえる必要があります。
とはいえ基本の書き方さえ習得すれば、あとはオプションや応用を学びながら徐々にスキルを深められるでしょう。

開発規模が拡大してコードが複雑になると、共通のfetchラッパ関数を作る方法や、並列リクエストの活用、ストリーミングなどの高度な機能を検討するフェーズに進みます。
そのときも、今回ご紹介した「エラーハンドリング」「認証」「CORS対策」の概念を押さえておけば、大きくつまずくことは少ないはずです。

ぜひ一度、手元のプロジェクトや簡単なサンプルを通じてfetchの挙動を確認してみてください。
実際にコードを書いてみることで、非同期通信の仕組みが腑に落ちるタイミングが来ると思います。
そして、fetchをマスターしておくとWeb開発の幅がさらに広がり、多彩な機能を実装できるようになるのではないでしょうか。

JavaScriptをマスターしよう

この記事で学んだJavaScriptの知識をさらに伸ばしませんか?
Udemyには、現場ですぐ使えるスキルを身につけられる実践的な講座が揃っています。