How to send multipart/mixed with axios
There is example of source code with request
const messages = body[endpoint].map(function (m) {
return {
'Content-Type': 'application/http',
body: 'GET ' + api + '/gmail/v1/users/me/' + endpoint + '/' + m.id + query + '\n'
}
})
const r = request({
method: 'POST',
url: api + '/batch/gmail/v1',
multipart: messages,
timeout: opts.timeout,
headers: {
'Authorization': 'Bearer ' + key,
'content-type': 'multipart/mixed'
}
})
from this library
https://github.com/SpiderStrategies/node-gmail-api
To solve this problem I used request catcher and wrote the following tests:
it('request catcher of request', () => {
const catcher = 'https://pl.requestcatcher.com/';
const messages = [1,2,3].map(function (index:number) {
return {
'Content-Type': 'application/http',
body: 'GET ' + `/gmail/v1/users/me/${index}\n`
}
})
const r = request({
method: 'POST',
url: catcher,
multipart: messages,
headers: {
'Authorization': 'Bearer KEY',
'content-type': 'multipart/mixed'
}
})
expect(r).toBeNull();
})
and saw the following request on catcher:

For axios there is not ready answer in all internet. This is my research:
You can use a FormData object in the browser. In Node.js the feature isn't supported yet by axios (see #789).
https://github.com/axios/axios/issues/803
[RFC2388] suggested that multiple files for a single form field be
transmitted using a nested "multipart/mixed" part. This usage is
deprecated.
https://datatracker.ietf.org/doc/html/rfc7578
info how to do it in request ( that is also depreciated )
https://github.com/request/request#multipartrelated
Function for generation boundary
https://github.com/form-data/form-data/blob/53adbd81e9bde27007b28083068f2fc8272614dc/lib/form_data.js#L347
So we can send multipart/mixed by this code
it('request catcher of axios', () => {
const catcher = 'https://pl.requestcatcher.com/';
const r = axios.post(catcher, `--3e50fc2b-7a5c-4565-99f2-1628010018be
Content-Type: application/http
GET /gmail/v1/users/me/1
--3e50fc2b-7a5c-4565-99f2-1628010018be
Content-Type: application/http
GET /gmail/v1/users/me/2
--3e50fc2b-7a5c-4565-99f2-1628010018be
Content-Type: application/http
GET /gmail/v1/users/me/3
--3e50fc2b-7a5c-4565-99f2-1628010018be--`,{
headers: {
'Authorization': 'Bearer KEY',
'content-type': 'multipart/mixed; boundary=3e50fc2b-7a5c-4565-99f2-1628010018be'
}
})
expect(r).not.toBeNull();
})
and now oly generation of boundary is problem to solve. Lets check differences between multipart/mixed and and multipart/form-data bounduaries.
This is exmple of bounduary from form-data package:
--------------------------942880057551350107765038
and this is from mixed (from request):
--3e50fc2b-7a5c-4565-99f2-1628010018be
and this is proper RFC
https://www.w3.org/Protocols/rfc1341/7_2_Multipart.html
so we can use this code
> `--${crypto.randomUUID()}`
'--7784084f-e129-44ea-aa64-8f480a26c75f'
And this is code that sends mutipart/mixed by axios
it('request catcher of axios', () => {
const catcher = 'https://pl.requestcatcher.com/';
interface SingleMultipartMixedMessage {
'Content-Type': string,
body: string
}
const messages = [1, 2, 3].map(function (index: number) {
return {
'Content-Type': 'application/http',
body: 'GET ' + `/gmail/v1/users/me/${index}\n`
}
})
const boundary = `--${crypto.randomUUID()}`
const body = messages.reduce((p: string, n: SingleMultipartMixedMessage) => {
return p + `\nContent-Type: ${n['Content-Type']}\n\n${n.body}\n${boundary}`
}, boundary)
const r = axios.post(catcher, body, {
headers: {
'Authorization': 'Bearer KEY',
'content-type': `multipart/mixed; boundary=${boundary.replace(/^--/, '')}`
}
})
expect(r).not.toBeNull();
})
and comparison of requests caught by caught between reference implementation [ request ] and our code [ axios ]

How to parse multipart mixed
- Get boundary from content-type header
- Add
-- before it
- Split body by this boundary
- Any part split by double
\n
- Parse any pair of headers and data individually