elbywan / wretch

A tiny wrapper built around fetch with an intuitive syntax. :candy:
MIT License
4.79k stars 96 forks source link

formData() helper method does not support appending data to the same key #119

Closed tonisostrat closed 2 years ago

tonisostrat commented 2 years ago

I seem to have run into a limitation of the formData() builder method. Our (Spring) back-end expects an array-type field containing all user-submitted files. It's trivial to achieve with the default FormData object:

const files = getCachedFiles();

const formData = new FormData();

files.forEach(file => formData.append("files", file));

getWretch()
  .url("")
  .body(formData)
  .post();

which produces the following multipart payload (only the relevant lines have been kept):

Content-Disposition: form-data; name="files"; filename="foo.txt"
Content-Disposition: form-data; name="files"; filename="bar.txt"

but it does not work with wretch's custom helper method:

const files = getCachedFiles();

getWretch()
  .url("")
  .formData({
    files: files,
  })
  .post();

which results in the following payload:

Content-Disposition: form-data; name="files[]"; filename="foo.txt"
Content-Disposition: form-data; name="files[]"; filename="bar.txt"

and the data on the back-end is null.

Obviously it's also impossible to add the files separately under the same key as the input for the method is an object:

const files = getCachedFiles();

getWretch()
  .url("")
  .formData({
    files: files[0],
    files: files[1], // invalid
  })
  .post();

Is there some part of the documentation that I've missed or is this a genuine limitation?

elbywan commented 2 years ago

Hey @tonisostrat,

Is there some part of the documentation that I've missed or is this a genuine limitation?

Passing the files array as a value of the formData object argument should do the trick.

const files = getCachedFiles();

getWretch()
  .url("")
  .formData({ files })
  .post();
tonisostrat commented 2 years ago

Thank you for the prompt reply!

Unfortunately passing the array in as-is does not work - if you take a look at my original post that is exactly what I did. Using the array generates a request body where the field name is files[] instead of just files.

I have put together a quick demonstration here. The queries obviously fail but if you open up your browser's console you can inspect the body content and the difference becomes evident.

elbywan commented 2 years ago

Unfortunately passing the array in as-is does not work - if you take a look at my original post that is exactly what I did. Using the array generates a request body where the field name is files[] instead of just files.

Ah I'm sorry my bad I replied too quickly 🤦.

So yeah in this case this is a limitation and you will need to use Formdata.append + .body instead of the .formData helper.

tonisostrat commented 2 years ago

Gotcha, at least I know I'm not going crazy :)

Is there any chance you'll consider implementing this functionality? The helper method is great and really cuts down on LoC compared to instantiating the FormData object, especially as it doesn't even have a fluent interface..

elbywan commented 2 years ago

Is there any chance you'll consider implementing this functionality? The helper method is great and really cuts down on LoC compared to instantiating the FormData object, especially as it doesn't even have a fluent interface..

So if I recall correctly the reason why the brackets ([]) are auto-appended for array properties is that I followed the MDN documentation stating that some languages like PHP are using this convention.

In retrospective I think that was a bad move, but changing it would be breaking.

I'm definitely planning to release 2.0 at some point and rewrite the whole lib, I just cannot tell when this will happen yet. But when it does I'll make sure to include this change 😉.

elbywan commented 2 years ago

Should be fixed with the v2.