FormData збирає дані форми у формат, який браузер відправляє так само, як звичайна HTML-форма, - зокрема з файлами.
const form = document.querySelector('#profile-form');
form.addEventListener('submit', async (event) => {
event.preventDefault();
const data = new FormData(form); // усі поля з атрибутом name, включно з файлами
data.append('source', 'web'); // додаткове поле
const response = await fetch(form.action, {
method: 'POST',
body: data,
headers: { Accept: 'application/json' },
});
});
Найчастіша помилка - вручну задати Content-Type:
headers: { 'Content-Type': 'multipart/form-data' } // зламає запит
Для multipart/form-data заголовок обов'язково містить межу (boundary), що розділяє частини: multipart/form-data; boundary=----WebKitFormBoundary.... Браузер генерує її сам, якщо тіло - FormData. Вказаний вручну заголовок межі не має - сервер не зможе розібрати запит.
Корисні методи:
data.get('email'); // значення поля
data.getAll('tags[]'); // усі значення поля з однаковою назвою
data.set('email', 'new@example.com'); // замінити
data.delete('password_confirmation');
Object.fromEntries(data); // в об'єкт (для полів без повторів)
Файли: <input type="file" name="avatar"> потрапляє у FormData автоматично. Додати файл чи Blob вручну - data.append('avatar', file, 'avatar.png').
Особливості з Laravel:
PUT/PATCHз файлами: PHP історично розбираєmultipart/form-dataлише дляPOST. Для оновлення з файлами відправляютьPOSTз підміною методу:data.append('_method', 'PUT');- CSRF: для форм, що відправляються fetch-ем, токен з поля
_tokenпотрапить уFormDataсам, якщо в Blade-формі є@csrf; Accept: application/json- щоб Laravel при помилці валідації повернув JSON 422 з полемerrors, а не редирект назад;- масиви називають як у HTML:
tags[],items[0][qty]- Laravel розбере їх у вкладені масиви.
Коли не FormData: якщо файлів немає, а API приймає JSON, простіше body: JSON.stringify(payload) з Content-Type: application/json.
Прогрес завантаження великого файлу fetch наразі не показує. Для смуги прогресу використовують XMLHttpRequest (подія upload.onprogress) або бібліотеку на кшталт axios, що використовує його всередині.