FormData API
debt(d7/e1/b3/t9)
Closest to 'only careful code review or runtime testing' (d7). The detection_hints note that automated detection is 'no' and ESLint is listed but cannot catch the semantic mistake of manually setting Content-Type or forgetting CSRF appends without a highly specific custom rule. The bug manifests as a broken upload at runtime — the request goes out but PHP receives nothing, which requires runtime testing or careful review to catch.
Closest to 'one-line patch or single-call swap' (e1). The quick_fix is exactly one change: remove the manually-set Content-Type header (or add one FormData.append call for CSRF). No refactoring needed — it's a single-line deletion or addition at the call site.
Closest to 'localised tax' (b3). The applies_to scope is web contexts only, and the pattern is confined to individual form-submission call sites. Each affected fetch/XHR call needs the correct pattern, but the rest of the codebase is unaffected. It doesn't impose a cross-cutting architectural burden.
Closest to 'catastrophic trap — the obvious way is always wrong' (t9). The misconception field explicitly states that the 'obvious' developer action — manually setting Content-Type: multipart/form-data — is exactly wrong and silently breaks the upload by omitting the browser-generated boundary parameter. This directly contradicts how Content-Type headers work everywhere else (where you set them explicitly), making it a classic t9 trap.
Also Known As
TL;DR
Explanation
FormData mirrors PHP's $_POST and $_FILES. new FormData(formElement) captures all fields automatically. Append extra data with .append(). Submit via fetch() with no Content-Type header — the browser sets multipart/form-data with the correct boundary automatically. PHP reads files via $_FILES and fields via $_POST as normal. Never set Content-Type: application/json when sending FormData — it breaks multipart encoding.
Common Misconception
Why It Matters
Common Mistakes
- Setting Content-Type header manually when using FormData
- Not appending CSRF token to FormData before submit
- Using JSON.stringify on FormData — it serialises to empty object
Code Examples
// Wrong — breaks multipart boundary:
fetch('/upload.php', {
method: 'POST',
headers: { 'Content-Type': 'multipart/form-data' }, // DO NOT SET
body: new FormData(form)
});
const fd = new FormData(document.querySelector('#upload-form'));
fd.append('_token', document.querySelector('meta[name=csrf-token]').content);
// No Content-Type header — browser sets it correctly:
const res = await fetch('/upload.php', { method: 'POST', body: fd });
const data = await res.json();
// PHP reads normally: $_POST['field'], $_FILES['file']