Перейти к основному содержимому

fetch

Метод fetch()​

Встроенный метод браузера для HTTP-запросов. Возвращает промис с объектом Response. Само тело ещё не данные: его читают отдельно, чаще всего через res.json(). Этот метод тоже возвращает промис.

Using the Fetch API

fetch(url, {
method, // по умолчанию GET
headers,
body: JSON.stringify(data), // для POST, PUT, PATCH
});

GET​

fetch('https://swapi.dev/api/planets/1')
.then(res => res.json())
.then(data => console.log('data', data))
.catch(error => console.error('error', error));

Свойства ответа​

Их смотрят в первом .then, до res.json().

  • res.ok — true, если статус от 200 до 299
  • res.status — код ответа, например 200 или 404
  • res.headers — заголовки ответа
const url = 'https://swapi.dev/api/planets/1';

fetch(url).then(res => {
console.log('ok', res.ok);
console.log('status', res.status);
console.log('headers', res.headers);

return res.json();
});

Статусы 404 и 500 не попадают в .catch. Туда попадают обрыв сети и ошибка разбора тела. Код ответа проверяют сами и при неуспехе пробрасывают ошибку:

fetch(url)
.then(res => {
if (!res.ok) {
throw new Error(String(res.status));
}

return res.json();
})
.then(data => console.log('data', data))
.catch(error => console.log('error', error));

При статусе 400 или 422 сервер часто присылает JSON с пояснением. Его читают до throw: после исключения тело уже не достать. Текст для пользователя обычно лежит в message, а error.message тогда содержит его, а не только код.

fetch(url)
.then(res => {
return res.json().then(data => {
if (!res.ok) {
throw new Error(data.message ?? String(res.status));
}

return data;
});
})
.then(data => console.log(data))
.catch(error => console.log(error.message));

Сохранение результата​

Присваивание внутри .then случается после ответа. Строка сразу после fetch переменную ещё не видит.

const url = 'https://swapi.dev/api/planets/1';
let save = null;

fetch(url)
.then(res => {
if (!res.ok) {
throw new Error(String(res.status));
}

return res.json();
})
.then(data => {
save = data;
console.log(save);
});

save; // null: запрос ещё не завершён

Чтобы отдать данные из функции, из неё возвращают промис. return внутри .then передаёт значение только следующему звену той же цепочки.

const getData = () => {
return fetch('https://swapi.dev/api/planets/1').then(res => {
if (!res.ok) {
throw new Error(String(res.status));
}

return res.json();
});
};

getData().then(data => {
console.log(data);
});

await пишут только внутри async-функции. В обычном скрипте на верхнем уровне будет SyntaxError.

const getPlanet = async () => {
const res = await fetch('https://swapi.dev/api/planets/1');

if (!res.ok) {
throw new Error(String(res.status));
}

return res.json();
};

getPlanet().then(data => console.log(data));

Строка const data = await getPlanet() в обычном скрипте не выполнится. Её оставляют внутри ещё одной async-функции. Сама data видна только там:

const showPlanet = async () => {
const data = await getPlanet();

console.log(data);
};

showPlanet();

Та же строка допустима на верхнем уровне ES-модуля. В браузере у тега скрипта для этого нужен type="module":

<script type="module">
const data = await getPlanet();

console.log(data);
</script>

В среде, где запускается фрагмент кода, включают top-level await в advanced settings. После этого const data = await getPlanet() работает без обёртки.

POST​

Тело передают строкой. Для JSON нужен заголовок Content-Type.

fetch('https://jsonplaceholder.typicode.com/posts', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
title: 'Title of post',
body: 'Post Body',
}),
})
.then(res => {
if (!res.ok) {
throw new Error(String(res.status));
}

return res.json();
})
.then(data => console.log(data))
.catch(error => console.log(error));

Параметры запроса​

fetch(url, {
method: 'POST', // GET, POST, PUT, PATCH, DELETE
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(data),
mode: 'cors', // cors, no-cors, same-origin
credentials: 'same-origin', // omit, same-origin, include
cache: 'default', // default, no-cache, reload, force-cache, only-if-cached
});

Чем читать тело​

Тело можно прочитать только один раз.

  • res.json() — JSON
  • res.text() — текст
  • res.blob() — файл
  • res.formData() — FormData