I am using axios to send a FormData object, containing a categories field, an array of strings, to an express server. I am using multer to parse the FormData recieved, however req.body.categories is a single string after parsing eg 'cat1, cat2, cat3', not an array of strings eg ['cat1', 'cat2', 'cat3']
This is the code i am using to send the FormData:
newProduct: {categories: string[]}
const formData = new FormData()
for (let key of Object.keys(newProduct)){
formData.append(key, newProduct[key])
}
const response = await axios.post('/api/products', formData)
I have tried to JSON.stringify(categories)
before appending this to the formdata but multer understandably just parsed this as a string aswell.
It feels 'icky' to just use categories.split('')
after multer has parsed the rest of the formdata so graciously for me, but is there another way or am i doing something wrong?
I am using axios to send a FormData object, containing a categories field, an array of strings, to an express server. I am using multer to parse the FormData recieved, however req.body.categories is a single string after parsing eg 'cat1, cat2, cat3', not an array of strings eg ['cat1', 'cat2', 'cat3']
This is the code i am using to send the FormData:
newProduct: {categories: string[]}
const formData = new FormData()
for (let key of Object.keys(newProduct)){
formData.append(key, newProduct[key])
}
const response = await axios.post('/api/products', formData)
I have tried to JSON.stringify(categories)
before appending this to the formdata but multer understandably just parsed this as a string aswell.
It feels 'icky' to just use categories.split('')
after multer has parsed the rest of the formdata so graciously for me, but is there another way or am i doing something wrong?
- This question is similar to: Can I append an array to 'formdata' in javascript?. If you believe it’s different, please edit the question, make it clear how it’s different and/or how the answers on that question are not helpful for your problem. – traynor Commented Mar 31 at 8:01
2 Answers
Reset to default 3To send arrays, you need to append each value individually using the same key.
const formData = new FormData()
for (const [key, value] of Object.entries(newProduct)){
if (Array.isArray(value)) {
for (const arrayItem of value) {
formData.append(key, arrayItem);
}
} else {
formData.append(key, value);
}
}
On the server, request.body.categories
will either be a string, if there is only one category, undefined
if there are zero categories, or an array of strings, if there are multiple categories. It is easiest to check if it is an array, and if not, to turn it into one.
app.post('/api/products', multer().none(), (request, response) => {
const {body} = request;
const categories = Array.isArray(body.categories)
// >= 2 items
? body.categories
: body.categories === undefined
// 0 items
? []
// 1 item
: [body.categories];
});
Unfortunately, there is no array type with multipart/form-data
. It will only be an array if a key has multiple values. That is why you need code to turn it into an array when there aren't multiple values. Furthermore, you need to handle the case where there are zero categories. With multer
, you can check with request.body.categories === undefined
and handle such cases as empty arrays.
If you're curious to see this in action, I have created a minimal Express app to test my answer. (You can't run this snippet, but only snippets can be hidden)
import express from 'express';
import multer from 'multer';
const app = express();
app.get('/', (_request, response) => {
response.send(`<!doctype html>
<script src="https://cdnjs.cloudflare/ajax/libs/axios/1.8.4/axios.min.js" integrity="sha512-2A1+/TAny5loNGk3RBbk11FwoKXYOMfAK6R7r4CpQH7Luz4pezqEGcfphoNzB7SM4dixUoJsKkBsB6kg+dNE2g==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<script type="module">
async function sendProduct(product) {
const formData = new FormData()
for (let [key, value] of Object.entries(product)){
if (Array.isArray(value)) {
for (const item of value) {
formData.append(key, item);
}
} else {
formData.append(key, value);
}
}
const response = await axios.post('/api/products', formData);
}
await sendProduct({categories: []});
await sendProduct({categories: ['abc']});
await sendProduct({categories: ['abc', 'def']});
</script>
`)
});
app.post('/api/products', multer().none(), (request, response) => {
const {body} = request;
console.log(body);
const categories = Array.isArray(body.categories)
// >= 2 items
? body.categories
: body.categories === undefined
// 0 items
? []
// 1 item
: [body.categories];
console.log(categories);
response.send(body);
});
app.listen(3031, () => {
console.log('listening on http://localhost:3031');
});
I would just use the built-in WHATWG Request.formData()
available in node
, deno
, or bun
together with JSON.parse()
on the server and with JSON.stringify()
, as you tried, on the client. Here's an example you can run in the browser, which can be used on the server:
(async () => {
let fd = new FormData();
fd.append("arr", JSON.stringify(['cat1', 'cat2', 'cat3']));
await new Response(fd).text()
.then((body) => {
console.log(body);
const boundary = body.slice(2, body.indexOf("\r\n"));
return new Response(body, {
headers: {
"Content-Type": `multipart/form-data; boundary=${boundary}`,
},
})
// What you do in the server
.formData()
.then((data) => {
// Here's your Array
console.log(JSON.parse(data.get("arr")));
return data;
}).catch((e) => {
throw e;
});
}).catch(console.warn);
})().catch(console.warn);