Fetch API を利用すると、リクエストやレスポンスといった HTTP のパイプラインを構成する要素を操作できるようになります。またfetch() メソッドを利用することで、非同期のネットワーク通信を簡単にわかりやすく記述できるようになります。

従来、このような機能は XMLHttpRequest によって提供されてきました。Fetch はそれのより良い代替で、Service Workers のような技術から簡単に利用することが可能です。また CORS や HTTP 拡張のような HTTP に関連する概念をまとめて定義してもいます。

fetch の仕様は jQuery.ajax() とは主に 2 つの点で異なっています:

  • fetch() から返される Promise は レスポンスが HTTP 404 や 500 を返していても HTTP エラーステータスを拒否しません。その代わりに正常に解決し (ok ステータスは false にセットされます)、そしてネットワークの失敗または何かがリクエストの完了を妨げた場合のみに拒否するでしょう。
  • 既定では, fetch はサーバーから cookies を送受信せず、その結果サイトがユーザーセッションの維持に依存している場合は認証されないリクエストになります (cookie を送るには、クレデンシャルの init オプションをセットしておく必要があります)。

fetch リクエストを作成する

基本的な fetch はとても簡単に作成できます。下のコードを見てください。

fetch('http://example.com/movies.json')
  .then(function(response) {
    return response.json();
  })
  .then(function(myJson) {
    console.log(myJson);
  });

これはネットワーク越しにJSONファイルを取得して コンソールに出力するスクリプトです。fetch() の最も簡単な使い方は 1 つの引数 — fetch() で取得したいリソースへのパス — のみをとり、Response オブジェクトを含む promise を返します。

これはもちろんただの HTTP レスポンスであり、実際のJSONではありません。response オブジェクトからJSONを抜き出すには、json() メソッドを使用する必要があります。(Body のミックスイン(訳注:php のトレイトのようなものです。)として定義されていて、それらは Request オブジェクトと Response オブジェクトの両方に実装されています。)

付記: Body ミックスインはボディの内容を他の mime タイプとして展開する似たようなメソッドを提供しています。詳細は Body セクションをご覧ください。

Fetch リクエストは、検索したリソースからの指示よりも Content Security Policyconnect-src ディレクティブによって制御されます。

リクエストにオプションを適用する

fetch() メソッドには 2 つ目の引数を適用することもできます。多数の設定をコントロールすることのできる init オブジェクトです。

すべての設定可能なオプションや詳細な説明を見るには fetch() を参照してください。

// Example POST method implementation:

postData('http://example.com/answer', {answer: 42})
  .then(data => console.log(data)) // JSON from `response.json()` call
  .catch(error => console.error(error))

function postData(url, data) {
  // 既定のオプションには * が付いています
  return fetch(url, {
    body: JSON.stringify(data), // must match 'Content-Type' header
    cache: 'no-cache', // *default, no-cache, reload, force-cache, only-if-cached
    credentials: 'same-origin', // include, same-origin, *omit
    headers: {
      'user-agent': 'Mozilla/4.0 MDN Example',
      'content-type': 'application/json'
    },
    method: 'POST', // *GET, POST, PUT, DELETE, etc.
    mode: 'cors', // no-cors, cors, *same-origin
    redirect: 'follow', // manual, *follow, error
    referrer: 'no-referrer', // *client, no-referrer
  })
  .then(response => response.json()) // parses response to JSON
}

クレデンシャルつきのリクエストを送る

ブラウザーにクレデンシャルの入ったリクエストを送るようにするには、クロスオリジンの呼び出しであっても、credentials: 'include'init オブジェクトに追加して fetch() メソッドに渡します。.

fetch('https://example.com', {
  credentials: 'include'  
})

リクエスト URL が呼び出しスクリプトと同一オリジンの場合だけクレデンシャルを送りたい場合、credentials: 'same-origin'を追加します。

// The calling script is on the origin 'https://example.com'

fetch('https://example.com', {
  credentials: 'same-origin'  
})

この代わりにブラウザーがリクエストにクレデンシャルを含んでないことを保証するには、credentials: 'omit'を使います。

fetch('https://example.com', {
  credentials: 'omit'  
})

JSON データをアップロードする

fetch() を使って JSON-エンコードしたデータを POST します。

var url = 'https://example.com/profile';
var data = {username: 'example'};

fetch(url, {
  method: 'POST', // or 'PUT'
  body: JSON.stringify(data), // data can be `string` or {object}!
  headers: new Headers({
    'Content-Type': 'application/json'
  })
}).then(res => res.json())
.catch(error => console.error('Error:', error))
.then(response => console.log('Success:', response));

ファイルをアップロードする

ファイルは HTML <input type="file" /> input 要素や、FormData() や、fetch()を使ってアップロードできます。

var formData = new FormData();
var fileField = document.querySelector("input[type='file']");

formData.append('username', 'abc123');
formData.append('avatar', fileField.files[0]);

fetch('https://example.com/profile/avatar', {
  method: 'PUT',
  body: formData
})
.then(response => response.json())
.catch(error => console.error('Error:', error))
.then(response => console.log('Success:', response));

fetch が成功したかチェックする

ネットワークエラーに遭遇すると fetch() promise は TypeError を返して reject 状態になります。サーバー側の CORS が適切に設定されていない場合も同様です(アクセス権の問題ですけどね) — 一方で例えば 404 はネットワークエラーを構成しません。fetch() が成功したかどうかの明確な判定をするには、プロミスが解決されて、Response.ok プロパティが true になっているかなどを確認します。次のようなコードになるでしょう。

fetch('flowers.jpg').then(function(response) {
  if(response.ok) {
    return response.blob();
  }
  throw new Error('Network response was not ok.');
}).then(function(myBlob) { 
  var objectURL = URL.createObjectURL(myBlob); 
  myImage.src = objectURL; 
}).catch(function(error) {
  console.log('There has been a problem with your fetch operation: ', error.message);
});

request オブジェクトを fetch に渡す

fetch() を呼ぶときにリクエストしたいリソースへのパスを渡す代わりに、Request() コンストラクタを使用して Request オブジェクトを作成して fetch() メソッドの引数として渡すこともできます。

var myHeaders = new Headers();

var myInit = { method: 'GET',
               headers: myHeaders,
               mode: 'cors',
               cache: 'default' };

var myRequest = new Request('flowers.jpg', myInit);

fetch(myRequest).then(function(response) {
  return response.blob();
}).then(function(myBlob) {
  var objectURL = URL.createObjectURL(myBlob);
  myImage.src = objectURL;
});

fetch() メソッドの引数と全く同じ引数を Request() に適用させることができます。また、request オブジェクトのコピーを作成するためにすでに存在する request オブジェクトを渡すこともできます。

var anotherRequest = new Request(myRequest, myInit);

これは、リクエストとレスポンスのボディを 1 つのみ使用しているので、大変役立ちます。必要であれば、init オプションを変化させながらリクエスト / レスポンスを再利用できるようにコピーします。コピーをは body が読まれる前でなければならず、コピーの中の body を読むとオリジナルのリクエストも既読にマークされます。

付記: clone() メソッドを利用してコピーを生成することもできます。これには、ほかのコピーメソッドと若干異なる意味があります — 古いリクエストの body がすでに読み込まれていた場合、前者は失敗しますが、clone() は失敗しません(レスポンスでも同じです)。

Headers

Headers インターフェースでは、Headers() コンストラクタを使用して、ヘッダーオブジェクトを作成することができます。ヘッダーオブジェクトはシンプルな複数の名前と値の Map です。

var content = "Hello World";
var myHeaders = new Headers();
myHeaders.append("Content-Type", "text/plain");
myHeaders.append("Content-Length", content.length.toString());
myHeaders.append("X-Custom-Header", "ProcessThisImmediately");

同じことはコンストラクターに配列の配列かオブジェクトリテラルを渡すことで達成できます:

myHeaders = new Headers({
  "Content-Type": "text/plain",
  "Content-Length": content.length.toString(),
  "X-Custom-Header": "ProcessThisImmediately",
});

ヘッダーの中身を見たり、検索することができます。

console.log(myHeaders.has("Content-Type")); // true
console.log(myHeaders.has("Set-Cookie")); // false
myHeaders.set("Content-Type", "text/html");
myHeaders.append("X-Custom-Header", "AnotherValue");
 
console.log(myHeaders.get("Content-Length")); // 11
console.log(myHeaders.get("X-Custom-Header")); // ["ProcessThisImmediately", "AnotherValue"]
 
myHeaders.delete("X-Custom-Header");
console.log(myHeaders.get("X-Custom-Header")); // [ ]

いくつかの操作は ServiceWorkers でしか役立ちませんが、ヘッダーを操作するためのより良い API を提供しています。

すべての Header メソッドは有効な HTTP ヘッダーではない名前が渡されたとき TypeError を投げます。immutable ガード(下記を参照)がかかっている場合も、TypeError を投げます。もしくはエラーを投げずに失敗します。例を見てください:

var myResponse = Response.error();
try {
  myResponse.headers.set("Origin", "http://mybank.com");
} catch(e) {
  console.log("銀行のふりをしないで下さい!");
}

ヘッダーの良い使用方法としては処理を行う前に、コンテンツタイプが正しいかどうか判定する等の使い方があります。例:

fetch(myRequest).then(function(response) {
    var contentType = response.headers.get("content-type");
    if(contentType && contentType.includes("application/json")) {
      return response.json();
    }
    throw new TypeError("Oops, we haven't got JSON!");
  })
  .then(function(json) { /* process your JSON further */ })
  .catch(function(error) { console.log(error); });

Guard

ヘッダーは、リクエストで送信でき、レスポンスで受信できます。また、どの情報が変更できる(または、すべき)かといったさまざまな制限があります。そのため、ヘッダーは guard プロパティを持っています。これはリクエストやレスポンスに含まれませんが、ヘッダーオブジェクトでできる変更操作に影響を与えます。

設定できるガード値には以下のものがあります:

  • none: デフォルト値です。
  • request: リクエスト(Request.headers)で使用できる値のみにヘッダーを保護する。
  • request-no-cors: Request.mode no-cors で生成されたリクエスト(Request.headers)で使用できる値のみにヘッダーを保護する。
  • response: レスポンス (Response.headers)で使用できる値のみにヘッダーを保護する。
  • immutable: 主に ServiceWorker で使用されます。ヘッダーを読み取り専用にします。

付記: request のカードされたヘッダーの Content-Length ヘッダーは追加や変更できない可能性があります。同様に、レスポンスヘッダに Set-Cookie を挿入することはできません。ServiceWorker は、同期レスポンスを経由してクッキーを設定できません。

Response オブジェクト

すでに見てきたように, Response インスタンスは fetch() プロミスが解決(resolve)されたときに返り値として渡されます。

下記はどんな response オブジェクトでも共通で使用できる response プロパティです:

  • Response.status — HTTP ステータスコードの整数値(デフォルト値は 200)
  • Response.statusText — HTTP ステータスコードのメッセージと一致する文字列 (デフォルト値は "OK")
  • Response.ok — 上述の例で使用したように、これは HTTP ステータスコードが 200 から 299 のうちに収まってるかどうかのショートハンドです。これは Boolean を返します。

Response オブジェクトは JavaScript で動的に作ることもできます。これは ServiceWorkers 内において非常に役立ちます。例えばリクエストを受け取ったときに respondWith() メソッドによってカスタマイズされたレスポンスを返すようなときに役立ちます。

var myBody = new Blob();

addEventListener('fetch', function(event) { // ServiceWorker intercepting a fetch
  event.respondWith(
    new Response(myBody, {
      headers: { "Content-Type" : "text/plain" }
    })
  );
});

Response() コンストラクタはオプションとして 2 つの引数をとることができます — レスポンスボディと初期化オブジェクト(Request() が受け取れるものと似ています。)です。

付記: 静的メソッド error() は単純にエラーレスポンスを返します。同様にredirect() メソッドも 指定した URL にリダイレクトするレスポンスを返します。これらは Service Workers にのみ関連しています。

Body

リクエストもレスポンスもボディを持っています。body は以下のタイプのいずれかのインスタンスです。

Body ミックスインは RequestResponse に実装されていて、コンテンツを抜き出すために以下のメソッドが定義されています。これらはすべて最終的に実際の中身で解決されるプロミスを返します。

これらは非テキストデータを XHR よりはるかに楽に扱うことができます。

Request ボディは、body パラメータを渡すことによって設定することができます。

var form = new FormData(document.getElementById('login-form'));
fetch("/login", {
  method: "POST",
  body: form
});

Request や Response (と fetch() 関数の拡張) は自動的にコンテンツタイプを決定しようとします。Request もまた、指定されていなければ自動で Content-Type ヘッダーを設定しようとします。

使用可能かどうかの判別

Fetch API が利用できるかどうかは、HeadersRequestResponsefetch() のいずれかが Window もしくは Worker のスコープで参照できるかどうかによって判断できます。判断を行っている例は次のようになります:

if (self.fetch) {
    // ここで fetch リクエストを実行
} else {
    // XMLHttpRequest で何か実行する?
}

Polyfill

Fetch がサポートされていないブラウザーを使うため、非サポートブラウザー用の機能を再生成する Fetch Polyfill が利用できます。

仕様

仕様書 策定状況 備考
Fetch 現行の標準 初期定義

ブラウザーの実装状況

We're converting our compatibility data into a machine-readable JSON format. This compatibility table still uses the old format, because we haven't yet converted the data it contains. Find out how you can help!

機能 Chrome Edge Firefox (Gecko) Internet Explorer Opera Safari (WebKit)
基本サポート 42 14 39 (39)
34 (34)[1]
52 (52)[2]
未サポート 29
28[1]
10.1
機能 Android Webview Chrome for Android Firefox Mobile (Gecko) IE Phone Opera Mobile Safari Mobile
基本サポート 42 42 (有) 未サポート ? 10.1

[1] この API は実装されていますが設定で無効化されています。

[2] Firefox 52 より前で、get() は指定されたヘッダー内の最初の値のみを返し、getAll() ですべての値を返します。Firefox 52 以降は、get() はすべての値を返すようになり、getAll() は削除されました。

関連項目

ドキュメントのタグと貢献者

最終更新者: dlwe,