Fetch API предоставляет интерфейс JavaScript для работы с запросами и ответами HTTP. Он также предоставляет глобальный метод fetch()
(en-US), который позволяет легко и логично получать ресурсы по сети асинхронно.
Подобная функциональность ранее достигалась с помощью XMLHttpRequest
. Fetch представляет собой лучшую альтернативу, которая может быть легко использована другими технологиями, такими как Service Workers
(en-US). Fetch также обеспечивает единое логическое место для определения других связанных с HTTP понятий, такие как CORS и расширения для HTTP.
Обратите внимание, fetch
спецификация отличается от jQuery.ajax()
в основном в двух пунктах:
- Promise возвращаемый вызовом
fetch()
не перейдёт в состояние «отклонено» из-за ответа HTTP, который считается ошибкой, даже если ответ HTTP 404 или 500. Вместо этого, он будет выполнен нормально (с значением false в статусеok
) и будет отклонён только при сбое сети или если что-то помешало запросу выполниться. - По умолчанию,
fetch
не будет отправлять или получать cookie файлы с сервера, в результате чего запросы будут осуществляться без проверки подлинности, что приведёт к неаутентифицированным запросам, если сайт полагается на проверку пользовательской сессии (для отправки cookie файлов в аргументе init options должно быть задано значение свойства credentials отличное от значения по умолчаниюomit
).
Примечание: 25 августа 2017 г. в спецификации изменилось значение по умолчанию свойства credentials на same-origin
. Firefox применяет это изменение с версии 61.0b13.
Базовый запрос на получение данных действительно прост в настройке. Взгляните на следующий код:
fetch('http://example.com/movies.json') .then((response) => { return response.json(); }) .then((data) => { console.log(data); });
Здесь мы забираем JSON файл по сети и выводим его содержимое в консоль. Самый простой способ использования fetch()
заключается в вызове этой функции с одним аргументом — строкой, содержащей путь к ресурсу, который вы хотите получить — которая возвращает promise, содержащее ответ (объект Response
).
Конечно, это просто HTTP-ответ, а не фактический JSON. Чтобы извлечь содержимое тела JSON из ответа, мы используем json()
(en-US) метод (определён миксином Body
, который реализован в объектах Request
и Response
.)
Примечание: Миксин Body имеет подобные методы для извлечения других типов контента; см. раздел Тело.
Fetch-запросы контролируются посредством директивы connect-src
(Content Security Policy (en-US)), а не директивой извлекаемых ресурсов.
Установка параметров запроса
Метод fetch()
может принимать второй параметр — объект init
, который позволяет вам контролировать различные настройки:
// Пример отправки POST запроса:
async function postData(url = '', data = {}) {
// Default options are marked with *
const response = await fetch(url, {
method: 'POST', // *GET, POST, PUT, DELETE, etc.
mode: 'cors', // no-cors, *cors, same-origin
cache: 'no-cache', // *default, no-cache, reload, force-cache, only-if-cached
credentials: 'same-origin', // include, *same-origin, omit
headers: {
'Content-Type': 'application/json'
// 'Content-Type': 'application/x-www-form-urlencoded',
},
redirect: 'follow', // manual, *follow, error
referrerPolicy: 'no-referrer', // no-referrer, *client
body: JSON.stringify(data) // body data type must match "Content-Type" header
});
return await response.json(); // parses JSON response into native JavaScript objects
}
postData('https://example.com/answer', { answer: 42 })
.then((data) => {
console.log(data); // JSON data parsed by `response.json()` call
});
С подробным описанием функции и полным списком параметров вы можете ознакомиться на странице fetch()
(en-US).
Отправка запроса с учётными данными
Чтобы браузеры могли отправлять запрос с учётными данными (даже для cross-origin запросов), добавьте credentials: 'include'
в объект init
, передаваемый вами в метод fetch()
:
fetch('https://example.com', {
credentials: 'include'
})
Если вы хотите отправлять запрос с учётными данными только если URL принадлежит одному источнику (origin) что и вызывающий его скрипт, добавьте credentials: ‘same-origin’.
// Вызывающий скрипт принадлежит источнику 'https://example.com'
fetch('https://example.com', {
credentials: 'same-origin'
})
Напротив, чтобы быть уверенным, что учётные данные не передаются с запросом, используйте credentials: ‘omit’:
fetch('https://example.com', {
credentials: 'omit'
})
Отправка данных в формате JSON
При помощи fetch()
(en-US) можно отправлять POST-запросы в формате JSON.
const url = 'https://example.com/profile';
const data = { username: 'example' };
try {
const response = await fetch(url, {
method: 'POST', // или 'PUT'
body: JSON.stringify(data), // данные могут быть 'строкой' или {объектом}!
headers: {
'Content-Type': 'application/json'
}
});
const json = await response.json();
console.log('Успех:', JSON.stringify(json));
} catch (error) {
console.error('Ошибка:', error);
}
Загрузка файла на сервер
На сервер можно загрузить файл, используя комбинацию HTML-элемента <input type="file" />
, FormData()
и fetch()
.
const formData = new FormData();
const fileField = document.querySelector('input[type="file"]');
formData.append('username', 'abc123');
formData.append('avatar', fileField.files[0]);
try {
const response = await fetch('https://example.com/profile/avatar', {
method: 'PUT',
body: formData
});
const result = await response.json();
console.log('Успех:', JSON.stringify(result));
} catch (error) {
console.error('Ошибка:', error);
}
Загрузка нескольких файлов на сервер
На сервер можно загрузить несколько файлов, используя комбинацию HTML-элемента <input type="file" multiple />
, FormData()
и fetch()
.
const formData = new FormData();
const photos = document.querySelector('input[type="file"][multiple]');
formData.append('title', 'Мой отпуск в Вегасе');
for (let i = 0; i < photos.files.length; i++) {
formData.append('photos', photos.files[i]);
}
try {
const response = await fetch('https://example.com/posts', {
method: 'POST',
body: formData
});
const result = await response.json();
console.log('Успех:', JSON.stringify(result));
} catch (error) {
console.error('Ошибка:', error);
}
Обработка текстового файла построчно
Фрагменты данных, получаемые из ответа, не разбиваются на строки автоматически (по крайней мере с достаточной точностью) и представляют собой не строки, а объекты Uint8Array
. Если вы хотите загрузить текстовый файл и обрабатывать его по мере загрузки построчно, то на вас самих ложится груз ответственности за обработку всех упомянутых моментов. Как пример, далее представлен один из способов подобной обработки с помощью создания построчного итератора (для простоты приняты следующие допущения: текст приходит в кодировке UTF-8 и ошибки получения не обрабатываются).
async function* makeTextFileLineIterator(fileURL) {
const utf8Decoder = new TextDecoder("utf-8");
let response = await fetch(fileURL);
let reader = response.body.getReader();
let {value: chunk, done: readerDone} = await reader.read();
chunk = chunk ? utf8Decoder.decode(chunk) : "";
let re = /n|r|rn/gm;
let startIndex = 0;
let result;
for (;;) {
let result = re.exec(chunk);
if (!result) {
if (readerDone) {
break;
}
let remainder = chunk.substr(startIndex);
({value: chunk, done: readerDone} = await reader.read());
chunk = remainder + (chunk ? utf8Decoder.decode(chunk) : "");
startIndex = re.lastIndex = 0;
continue;
}
yield chunk.substring(startIndex, result.index);
startIndex = re.lastIndex;
}
if (startIndex < chunk.length) {
//последняя строка не имеет символа перевода строки в конце
yield chunk.substr(startIndex);
}
}
for await (let line of makeTextFileLineIterator(urlOfFile)) {
processLine(line);
}
Проверка успешности запроса
В методе fetch()
(en-US) promise будет отклонён (reject) с TypeError
, когда случится ошибка сети или не будет сконфигурирован CORS на стороне запрашиваемого сервера, хотя обычно это означает проблемы доступа или аналогичные — для примера, 404 не является сетевой ошибкой. Для достоверной проверки успешности fetch() будет включать проверку того, что promise успешен (resolved), затем проверку того, что значение свойства Response.ok
(en-US) является true. Код будет выглядеть примерно так:
try {
const response = await fetch('flowers.jpg');
if (!response.ok) {
throw new Error('Ответ сети был не ok.');
}
const myBlob = await response.blob();
const objectURL = URL.createObjectURL(myBlob);
myImage.src = objectURL;
} catch (error) {
console.log('Возникла проблема с вашим fetch запросом: ', error.message);
}
Составление своего объекта запроса
Вместо передачи пути ресурса, который вы хотите запросить вызовом fetch(), вы можете создать объект запроса, используя конструктор Request()
(en-US), и передать его в fetch() аргументом:
const myHeaders = new Headers();
const myInit = {
method: 'GET',
headers: myHeaders,
mode: 'cors',
cache: 'default'
};
const myRequest = new Request('flowers.jpg', myInit);
const response = await fetch(myRequest);
const myBlob = await response.blob();
const objectURL = URL.createObjectURL(myBlob);
myImage.src = objectURL;
Конструктор Request() принимает точно такие же параметры, как и метод fetch(). Вы даже можете передать существующий объект запроса для создания его копии:
const anotherRequest = new Request(myRequest, myInit);
Довольно удобно, когда тела запроса и ответа используются единожды (прим.пер.: «are one use only»). Создание копии как показано позволяет вам использовать запрос/ответ повторно, при изменении опций init, при желании. Копия должна быть сделана до прочтения тела, а чтение тела в копии также пометит его прочитанным в исходном запросе.
Примечание: Также есть метод clone()
(en-US), создающий копии. Оба метода создания копии прекратят работу с ошибкой если тело оригинального запроса или ответа уже было прочитано, но чтение тела клонированного ответа или запроса не приведёт к маркировке оригинального.
Заголовки
Интерфейс Headers
(en-US) позволяет вам создать ваш собственный объект заголовков через конструктор Headers()
(en-US). Объект заголовков — простая мультикарта имён-значений:
const content = 'Hello World';
const myHeaders = new Headers();
myHeaders.append('Content-Type', 'text/plain');
myHeaders.append('Content-Length', content.length.toString());
myHeaders.append('X-Custom-Header', 'ProcessThisImmediately');
То же может быть достигнуто путём передачи массива массивов или литерального объекта конструктору:
const 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
(en-US), но они предоставляют более удобный API для манипуляции заголовками.
Все методы Headers выбрасывают TypeError, если имя используемого заголовка не является валидным именем HTTP Header. Операции мутации выбросят TypeError если есть защита от мутации (смотрите ниже) (прим.пер.: «if there is an immutable guard»). В противном случае они прерываются молча. Например:
const myResponse = Response.error();
try {
myResponse.headers.set('Origin', 'http://mybank.com');
} catch (e) {
console.log('Не могу притвориться банком!');
}
Хорошим вариантом использования заголовков является проверка корректности типа контента перед его обработкой. Например:
try {
const response = await fetch(myRequest);
const contentType = response.headers.get('content-type');
if (!contentType || !contentType.includes('application/json')) {
throw new TypeError("Ой, мы не получили JSON!");
}
const json = await response.json();
/_ Дальнейшая обработка JSON _/
} catch (error) {
console.log(error);
}
Защита
С тех пор как заголовки могут передаваться в запросе, приниматься в ответе и имеют различные ограничения в отношении того, какая информация может и должна быть изменена, заголовки имеют свойство guard. Это не распространяется на Web, но влияет на то, какие операции мутации доступны для объекта заголовков.
Возможные значения:
none: по умолчанию.request: защита объекта заголовков, полученного по запросу (Request.headers
(en-US)).request-no-cors: защита объекта заголовков, полученного по запросу созданного с Request.mode
no-cors.response: защита Headers полученных от ответа (Response.headers
(en-US)).immutable: в основном, используется в ServiceWorkers; делает объект заголовков read-only.
Примечание: вы не можете добавить или установить request защищаемые Headers’ заголовок Content-Length. Аналогично, вставка Set-Cookie в заголовок ответа недопустимо: ServiceWorkers не допускают установки cookies через синтезированные ответы.
Объекты ответа
Как вы видели выше, экземпляр Response
будет возвращён когда fetch() промис будет исполнен.
Свойства объекта-ответа которые чаще всего используются:
Response.status
(en-US) — Целочисленное (по умолчанию 200) содержит код статуса ответа.Response.statusText
(en-US) — Строка (по умолчанию»OK»), которая соответствует HTTP коду статуса.Response.ok
(en-US) — как сказано ранее, это короткое свойство для упрощения проверки на то что статус ответа находится где-то между 200-299 включительно. Это свойство типа Boolean
(en-US).
Они так же могут быть созданы с помощью JavaScript, но реальная польза от этого есть только при использовании сервис-воркеров
(en-US), когда вы предоставляете собственный ответ на запрос с помощью метода respondWith()
(en-US):
const myBody = new Blob();
addEventListener('fetch', function(event) {
// ServiceWorker перехватывает fetch
event.respondWith(
new Response(myBody, {
headers: { 'Content-Type': 'text/plain' }
})
);
});
Конструктор Response()
принимает два необязательных аргумента — тело для ответа и объект init (аналогичный тому, который принимает Request()
(en-US))
Примечание: Метод error()
(en-US) просто возвращает ответ об ошибке. Аналогично, redirect()
(en-US) возвращает ответ, приводящий к перенаправлению на указанный URL. Они также относятся только к Service Workers.
Тело
Запрос и ответ могут содержать данные тела. Тело является экземпляром любого из следующих типов:
ArrayBuffer
ArrayBufferView
(en-US) (Uint8Array и подобные)Blob
/FilestringURLSearchParams
FormData
Body
примесь определяет следующие методы для извлечения тела (реализованы как для Request
так и для Response
). Все они возвращают promise, который в конечном итоге исполняется и выводит содержимое.
arrayBuffer()
(en-US)blob()
(en-US)json()
(en-US)text()
(en-US)formData()
(en-US)
Это делает использование нетекстовых данных более лёгким, чем при XMR.
В запросе можно установить параметры для отправки тела запроса:
const form = new FormData(document.getElementById('login-form'));
fetch('/login', {
method: 'POST',
body: form
});
Параметры request и response (and by extension the fetch() function), по возможности возвращают корректные типы данных. Параметр request также автоматически установит Content-Type в заголовок, если он не был установлен из словаря.
Функция обнаружения
Поддержка Fetch API может быть обнаружена путём проверки наличия Headers
(en-US), Request
, Response
или fetch()
(en-US) в области видимости Window
или Worker
. Для примера:
if (self.fetch) {
// запустить мой fetch запрос здесь
} else {
// Сделать что-то с XMLHttpRequest?
}
Полифил
BCD tables only load in the browser
Для того, чтобы использовать Fetch в неподдерживаемых браузерах, существует Fetch Polyfill , который воссоздаёт функциональность для не поддерживающих браузеров.
СпецификацииSpecification Status CommentFetch Живой стандарт Initial definitionСовместимость браузера
Смотрите такжеServiceWorker APIHTTP access control (CORS)HTTPFetch polyfillFetch examples on Github`
Время прочтения
5 мин
Просмотры 268K
Прощай, XMLHttpRequest!
fetch()
позволяет вам делать запросы, схожие с XMLHttpRequest (XHR). Основное отличие заключается в том, что Fetch API использует Promises (Обещания), которые позволяют использовать более простое и чистое API, избегать катастрофического количества callback’ов и необходимости помнить API для XMLHttpRequest.
Fetch API стал доступен пользователям вместе с Service Worker’ами в global скоупе в Chrome 40, однако уже в версии 42 он станет доступен в скоупе window. Разумеется, для всех остальных браузеров, которые пока ещё не поддерживают fetch существует полифил от GitHub, который доступен уже сегодня.
Простой Fetch запрос
Давайте начнём со сравнения простого примера, реализованного с XMLHttpRequest
и fetch
. Всё, что мы будем делать в этом примере — сделаем запрос на URL, получим ответ и распарсим его как JSON.
XMLHttpRequest
Пример с XMLHttpRequest потребует от нас установить два обработчика событий на success
и error
, а так же вызвать два метода: open()
и send()
. Пример из MDN документации:
function reqListener() {
var data = JSON.parse(this.responseText);
console.log(data);
}
function reqError(err) {
console.log('Fetch Error :-S', err);
}
var oReq = new XMLHttpRequest();
oReq.onload = reqListener;
oReq.onerror = reqError;
oReq.open('get', './api/some.json', true);
oReq.send();
Fetch
Наш fetch запрос будет выглядеть так:
fetch('./api/some.json')
.then(
function(response) {
if (response.status !== 200) {
console.log('Looks like there was a problem. Status Code: ' +
response.status);
return;
}
// Examine the text in the response
response.json().then(function(data) {
console.log(data);
});
}
)
.catch(function(err) {
console.log('Fetch Error :-S', err);
});
Первым делом мы проверяем статус ответа и проверяем, успешно ли выполнился запрос (ожидаем 200 статус). Если всё хорошо, то парсим ответ как JSON.
Ответом fetch()
является Stream-объект. Это означает, что в результате вызова метода json()
мы получим Promise, т.к. чтение из подобного объекта является асинхронным.
Метаданные ответа
В предыдущем примере мы изучили, как можно проверить статус объекта ответа и конвентировать сам ответ в JSON. Остальные метаданные, к которым вы возможно получить доступ (например, заголовки), приведены ниже:
fetch('users.json').then(function(response) {
console.log(response.headers.get('Content-Type'));
console.log(response.headers.get('Date'));
console.log(response.status);
console.log(response.statusText);
console.log(response.type);
console.log(response.url);
});
Типы ответа
Когда мы делаем fetch-запрос, ответу будет дан тип «basic», «cors» или «opaque». Эти «типы» указывают на то, с какого ресурса пришли данные и могут быть использованы для того, чтобы определить процесс обработки данных.
Когда запрос сделан на ресурс, находящимся на том же origin (имеется ввиду, что запрос выполняется в рамках одного сайта. прим. пер.), ответ будет содержать тип «базовый» и для такого запроса не будет никаких ограничений.
Если запрос сделан с одного origin’а на другой (кроссдоменный запрос), который, в свою очередь, вернул CORS заголовки, тогда типом будет являться «cors». Объекты с типами «cors» и «basic» почти идентичны, однако «cors» несколько ограничивает метаданные, к которым может быть получен доступ до «Cache-Control», «Content-Language», «Content-Type», «Expires», «Last-Modified», и «Pragma».
Что касается «opaque» — то он приходит в случаях, когда выполняется CORS запрос, но удаленный ресурс не возвращает CORS заголовки. Данный тип запроса не предоставляет доступ данным или заголовку статуса, поэтому мы не имеем возможности судить о результате выполнения запроса. В рамках текущей имплементации fetch()
не представляется возможности выполнять CORS запросы из скоупа window, и вот здесь написано почему. Эта функциональность должна быть добавлена, как только Cache API станет доступным из объекта window.
Вы можете определить ожидаемый режим запроса, тем самым фильтруя результаты запросов с неподходящим типом. Режим запроса может быть установлен на следующий:
— “same-origin” успешно выполняется только для запросов на тот же самый origin, все остальные запросы будут отклонены.
— “cors” работает так же, как «same-origin» + добавляет возможность создавать запросы к сторонним сайтам, если они возвращают соответствующие CORS заголовки.
— “cors-with-forced-preflight” работает так же, как «cors», но перед запросом всегда отсылает тестовый запрос на проверку.
— “no-cors” используется, когда необходимо выполнить запрос к origin, который не отсылает CORS заголовки и результатом выполнения является объект с типом «opaque». Как говорилось выше, в данный момент это невозможно в скоупе window.
Чтобы определить режим запроса, добавьте объект опций вторым параметром к запросу и установите «mode» в этом объекте:
fetch('http://some-site.com/cors-enabled/some.json', {mode: 'cors'})
.then(function(response) {
return response.text();
})
.then(function(text) {
console.log('Request successful', text);
})
.catch(function(error) {
log('Request failed', error)
});
Цепочки Promises
Одной из прекрасных особенностей Promise’ов является возможность группировать их в цепочки. Если говорить о них в скоупе fetch()
, то они позволяют нам «шарить» логику между запросами.
Если вы работаете с JSON API, вам потребуется проверить статус и распарсить JSON для каждого ответа. Вы можете упростить свой код, определив парсинг статуса и JSON как раздельные функции, которые вернут Promise’ы. Вам останется подумать только об обработке самих данных и, разумеется, исключений.
function status(response) {
if (response.status >= 200 && response.status < 300) {
return Promise.resolve(response)
} else {
return Promise.reject(new Error(response.statusText))
}
}
function json(response) {
return response.json()
}
fetch('users.json')
.then(status)
.then(json)
.then(function(data) {
console.log('Request succeeded with JSON response', data);
}).catch(function(error) {
console.log('Request failed', error);
});
Мы определяем функцию, которая проверяет response.status
и возвращает результат: Promise.resolve()
или Promise.reject()
. Это первый вызванный метод в нашей цепочке, и если он успешно завершается(Promise.resolve()
), то вызывается следующий за ним метод — fetch()
, который, в свою очередь, опять возвращает Promise от response.json()
. После этого вызова, в случае удачного выполнения, у нас будет готовый JSON объект. Если парсинг провалится, Promise будет отменен и сработает условие возникновения исключения.
Но самое лучшее здесь — это возможность переиспользовать такой код для всех fetch-запросов в приложении. Такой код проще поддерживать, читать и тестировать.
POST запрос
Уже давно никого не удивишь необходимостью использовать POST метод с передачей параметров в «теле» запроса для работы с API.
Чтобы осуществить такой запрос, мы должны указать соответствующие параметры в объекте настроек fetch()
:
fetch(url, {
method: 'post',
headers: {
"Content-type": "application/x-www-form-urlencoded; charset=UTF-8"
},
body: 'foo=bar&lorem=ipsum'
})
.then(json)
.then(function (data) {
console.log('Request succeeded with JSON response', data);
})
.catch(function (error) {
console.log('Request failed', error);
});
Посылаем учётные данные через Fetch-запрос
Если вы хотите отправить запрос с каким-либо учётными данными (например, с cookie), вам следует установить `credentials` в опциях запроса на «include»:
fetch(url, {
credentials: 'include'
})
FAQ
Могу ли я отменить fetch()
запрос?
В настоящий момент это невозможно, но это активно обсуждается на GitHub
Существует ли полифил?
Да
Почему «no-cors» реализован для service workers, но не для window?
Это было сделано из соображений безопасности. Подробнее можно ознакомиться здесь.
← flaviocopes.com
Updated
Apr 15 2019
- Introduction to the Fetch API
- Using Fetch
- Catching errors
- Response Object
- Metadata
- headers
- status
- statusText
- url
- Body content
- Metadata
- Request Object
- Request headers
- POST Requests
- How to cancel a fetch request
- Looking for more?
Introduction to the Fetch API
Since IE5 was released in 1998, we’ve had the option to make asynchronous network calls in the browser using XMLHttpRequest (XHR).
Quite a few years after this, GMail and other rich apps made heavy use of it, and made the approach so popular that it had to have a name: AJAX.
Working directly with the XMLHttpRequest has always been a pain and it was almost always abstracted by some library, in particular jQuery has its own helper functions built around it:
jQuery.ajax()
jQuery.get()
jQuery.post()
and so on.
They had a huge impact on making this more accessible in particular with regards to making sure all worked on older browsers as well.
The Fetch API, has been standardized as a modern approach to asynchronous network requests, and uses Promises as a building block.
Fetch has a good support across the major browsers, except IE.
The polyfill https://github.com/github/fetch released by GitHub allows us to use fetch
on any browser.
Using Fetch
Starting to use Fetch for GET requests is very simple:
fetch('/file.json')
and you’re already using it: fetch is going to make an HTTP request to get the file.json
resource on the same domain.
As you can see, the fetch
function is available in the global window
scope.
Now let’s make this a bit more useful, let’s actually see what the content of the file is:
fetch('./file.json')
.then(response => response.json())
.then(data => console.log(data))
Calling fetch()
returns a promise. We can then wait for the promise to resolve by passing a handler with the then()
method of the promise.
That handler receives the return value of the fetch
promise, a Response object.
We’ll see this object in detail in the next section.
Catching errors
Since fetch()
returns a promise, we can use the catch
method of the promise to intercept any error occurring during the execution of the request, and the processing done in the then
callbacks:
fetch('./file.json')
.then(response => {
//...
})
.catch(err => console.error(err))
Another way of catching errors is to manage them in the first then
:
fetch('./file.json')
.then(response => {
if (!response.ok) { throw Error(response.statusText) }
return response
})
.then(response => {
//...
})
Response Object
The Response Object returned by a fetch()
call contains all the information about the request and the response of the network request.
Metadata
Accessing the headers
property on the response
object gives you the ability to look into the HTTP headers returned by the request:
fetch('./file.json').then(response => {
console.log(response.headers.get('Content-Type'))
console.log(response.headers.get('Date'))
})
status
This property is an integer number representing the HTTP response status.
- 101, 204, 205, or 304 is a null body status
- 200 to 299, inclusive, is an OK status (success)
- 301, 302, 303, 307, or 308 is a redirect
fetch('./file.json').then(response => console.log(response.status))
statusText
statusText
is a property representing the status message of the response. If the request is successful, the status is OK
.
fetch('./file.json').then(response => console.log(response.statusText))
url
url
represents the full URL of the property that we fetched.
fetch('./file.json').then(response => console.log(response.url))
Body content
A response has a body, accessible using several methods:
text()
returns the body as a stringjson()
returns the body as a JSON-parsed objectblob()
returns the body as a Blob objectformData()
returns the body as a FormData objectarrayBuffer()
returns the body as anArrayBuffer
object
All those methods return a promise. Examples:
fetch('./file.json')
.then(response => response.text())
.then(body => console.log(body))
fetch('./file.json')
.then(response => response.json())
.then(body => console.log(body))
The same can be written using the ES2017 async functions:
;(async () => {
const response = await fetch('./file.json')
const data = await response.json()
console.log(data)
})()
Request Object
The Request object represents a resource request, and it’s usually created using the new Request()
API.
Example:
const req = new Request('/api/todos')
The Request object offers several read-only properties to inspect the resource request details, including
method
: the request’s method (GET, POST, etc.)url
: the URL of the request.headers
: the associated Headers object of the requestreferrer
: the referrer of the requestcache
: the cache mode of the request (e.g., default, reload, no-cache).
And exposes several methods including json()
, text()
and formData()
to process the body of the request.
The full API can be found at https://developer.mozilla.org/docs/Web/API/Request
Being able to set the HTTP request header is essential, and fetch
gives us the ability to do this using the Headers object:
const headers = new Headers()
headers.append('Content-Type', 'application/json')
or:
const headers = new Headers({
'Content-Type': 'application/json'
})
To attach the headers to the request, we use the Request object, and pass it to fetch()
instead of passing the URL.
Instead of:
fetch('./file.json')
we do
const request = new Request('./file.json', {
headers: new Headers({
'Content-Type': 'application/json'
})
})
fetch(request)
The Headers object is not limited to setting value, but we can also query it:
headers.has('Content-Type')
headers.get('Content-Type')
and we can delete a header that was previously set:
headers.delete('X-My-Custom-Header')
POST Requests
Fetch also allows to use any other HTTP method in your request: POST, PUT, DELETE or OPTIONS.
Specify the method in the method property of the request, and pass additional parameters in the header and in the request body:
Example of a POST request:
const options = {
method: 'post',
headers: {
'Content-type': 'application/x-www-form-urlencoded; charset=UTF-8'
},
body: 'name=Flavio&test=1'
}
fetch(url, options).catch(err => {
console.error('Request failed', err)
})
How to cancel a fetch request
For a few years after fetch
was introduced, there was no way to abort a request once opened.
Now we can, thanks to the introduction of AbortController
and AbortSignal
, a generic API to notify abort events
You integrate this API by passing a signal as a fetch parameter:
const controller = new AbortController()
const signal = controller.signal
fetch('./file.json', { signal })
You can set a timeout that fires an abort event 5 seconds after the fetch request has started, to cancel it:
setTimeout(() => controller.abort(), 5 * 1000)
Conveniently, if the fetch already returned, calling abort()
won’t cause any error.
When an abort signal occurs, fetch will reject the promise with a DOMException
named AbortError
:
fetch('./file.json', { signal })
.then(response => response.text())
.then(text => console.log(text))
.catch(err => {
if (err.name === 'AbortError') {
console.error('Fetch aborted')
} else {
console.error('Another error', err)
}
})
Looking for more?
Working with network is hard, right? You might find that the Axios JavaScript library might be a better fit for your needs with some additional features built upon Fetch. Check it out!
Check out my books
250k people get my newsletter
Want to get in front of them? Sponsor