To quote MDN on FormData (emphasis mine):
The
FormDatainterface provides a way to easily construct a set of key/value pairs representing form fields and their values, which can then be easily sent using theXMLHttpRequest.send()method. It uses the same format a form would use if the encoding type were set to"multipart/form-data".
So when using FormData you are locking yourself into multipart/form-data. There is no way to send a FormData object as the body and not sending data in the multipart/form-data format.
If you want to send the data as application/x-www-form-urlencoded you will either have to specify the body as an URL-encoded string, or pass a URLSearchParams object. The latter unfortunately cannot be directly initialized from a form element. If you don’t want to iterate through your form elements yourself (which you could do using HTMLFormElement.elements), you could also create a URLSearchParams object from a FormData object:
const data = new URLSearchParams();
for (const pair of new FormData(formElement)) {
data.append(pair[0], pair[1]);
}
fetch(url, {
method: 'post',
body: data,
})
.then(…);
Note that you do not need to specify a Content-Type header yourself.
As noted by monk-time in the comments, you can also create URLSearchParams and pass the FormData object directly, instead of appending the values in a loop:
const data = new URLSearchParams(new FormData(formElement));
This still has some experimental support in browsers though, so make sure to test this properly before you use it.