Чтобы fetch сделал не GET, а POST, нужно передать вторым аргументом объект с опциями.
Базовый POST с JSON
const response = await fetch('https://jsonplaceholder.typicode.com/posts', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
title: 'Привет, мир',
body: 'Это мой первый пост.',
userId: 1,
}),
});
if (!response.ok) throw new Error(`HTTP ${response.status}`);
const created = await response.json();
console.log(created); // {id: 101, title: ..., body: ..., userId: 1}
Разберём по строкам.
Поле method
Имя HTTP-метода. Если опустить — по умолчанию GET. Для POST, PUT, PATCH, DELETE — указываем явно.
Имя метода чувствительно к регистру: 'POST' работает, 'post' — нет. По соглашению пишем заглавными.
Поле headers
Заголовки запроса как объект. Самый важный для POST с JSON — Content-Type: application/json. Он сообщает серверу, что в теле лежит JSON и его нужно парсить именно так. Без этого заголовка многие сервера откажут или поймут тело как чистый текст.
Что ещё кладём в headers:
- Authorization — если API требует токен. Подробно — в модуле 7.
- Accept — формат ожидаемого ответа.
- Любые кастомные заголовки вида X-Request-ID, X-Client-Version — если API их ждёт.
Поле body
body — то, что поедет в тело запроса. Должно быть строкой (или одним из специальных типов: FormData, Blob, URLSearchParams — о них ниже).
JS-объект напрямую положить нельзя. Если сделать body: {title: 'X'}, fetch приведёт объект к строке через toString() — и пришлёт серверу буквальную строку "[object Object]". Знаменитый баг джунов; ловится один раз — и больше никогда.
Правильно — body: JSON.stringify(объект). Эта пара (Content-Type: application/json + JSON.stringify) встречается практически в каждом POST/PUT/PATCH-запросе из JS. Привыкайте писать вместе.
Что вернёт сервер
На удачный POST в коллекцию обычно приходит код 201 (Created). В теле — созданный ресурс, с присвоенным сервером id. В заголовке Location — URL созданного ресурса.
Иногда возвращают 200 (если сервер считает, что ресурс «применён», а не «создан») или 204 (если ничего возвращать не нужно). Конкретный код описан в документации API.
Отправка формы вместо JSON
Не каждое API ждёт JSON. Иногда (особенно у старых сервисов и форм авторизации в OAuth) тело должно быть в формате «url-encoded form» — тот же синтаксис, что у query-параметров, только в теле.
const response = await fetch('https://example.com/login', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: new URLSearchParams({
username: 'anna',
password: '12345',
}),
});
А если в форме есть файлы — нужна FormData:
const data = new FormData();
data.append('title', 'Аватарка');
data.append('avatar', fileInput.files[0]);
const response = await fetch('/upload', {
method: 'POST',
body: data,
// НЕ ставим Content-Type вручную! Браузер сам поставит
// multipart/form-data с правильным boundary.
});
Важный нюанс с FormData: не ставьте Content-Type руками. Браузер сам сформирует строку вида multipart/form-data; boundary=----WebKitFormBoundary..., в которой boundary — уникальный разделитель кусков. Если поставите свой, серверу будет нечем парсить и он развалится.
Краткая шпаргалка
| Что отправляем | Content-Type | body |
| JSON | application/json | JSON.stringify(obj) |
| Форма без файлов | application/x-www-form-urlencoded | new URLSearchParams(obj) |
| Форма с файлами | не ставим вручную | new FormData() |
| Простой текст | text/plain | 'строка' |
| Бинарные данные | application/octet-stream | Blob или ArrayBuffer |
В 95% работы с REST API используется первый вариант — JSON. Остальные пригодятся в специальных случаях.