I am trying to implement apollo-datasource-rest
to handle image uploading by URL via Imgur's API (documentation here: https://apidocs.imgur.com/)
I initially was getting a 400
error which read We don't support that file type!
, and determined that it was due to apollo-datasource-rest
automatically setting the Content-Type
to application/json
. After fixing that issue using the form-data
npm package, here's what my code looks like:
class ImgurAPI extends RESTDataSource {
constructor() {
super();
this.baseURL = "https://api.imgur.com/3/";
}
willSendRequest(request) {
request.headers.set("Content-Type", "multipart/form-data");
request.headers.set(
"Authorization",
`Client-ID ${process.env.IMGUR_CLIENT_ID}`
);
console.log(request);
}
async uploadImageFromUrl(url) {
const formData = new FormData();
formData.append("image", url);
return this.post("upload", formData);
}
}
I now no longer get the We don't support that file type!
error, but I still get a 400
response with a status text of just Bad Request
. The console.log()
from the previous code snippet prints this:
{
method: 'POST',
path: 'upload',
body: FormData {
_overheadLength: 104,
_valueLength: 80,
_valuesToMeasure: [],
writable: false,
readable: true,
dataSize: 0,
maxDataSize: 2097152,
pauseStreams: true,
_released: false,
_streams: [
'----------------------------594660553626244976225816\r\n' +
'Content-Disposition: form-data; name="image"\r\n' +
'\r\n',
'https://upload.wikimedia.org/wikipedia/commons/a/a0/Sunflower_as_gif_websafe.gif',
[Function: bound ]
],
_currentStream: null,
_insideLoop: false,
_pendingNext: false,
_boundary: '--------------------------594660553626244976225816'
},
params: URLSearchParams {},
headers: Headers {
[Symbol(map)]: [Object: null prototype] {
'Content-Type': [Array],
Authorization: [Array]
}
}
}
What am I missing here? The API seems to be accepting my form data so I would think that maybe there's some issue with one of the other headers, but in Postman it looks like there's only a few required headers, most of which are calculated (e.g. Content-Length
) and so I assume apollo-datasource-rest
must be handling that.
After doing some more research into multipart/form-data
, I found that the boundary
parameter is mandatory, and must be added to the Content-Type
value in the request for the server to be able to parse the payload. Furthermore I am unable to manually set it as a parameter in the request (at least not in a way that actually works). Postman normally calculates this field when the request is sent, but apollo-datasource-rest
doesn't automatically handle that.
Changing the Content-Type
to application/x-www-form-urlencoded
and using a url encoded string instead of form-data
fixed the issue.
Here's the updated code:
willSendRequest(request) {
request.headers.set("Content-Type", `application/x-www-form-urlencoded`);
request.headers.set(
"Authorization",
`Client-ID ${process.env.IMGUR_CLIENT_ID}`
);
console.log(request);
}
async uploadImageFromUrl(url) {
const formData = `image=${url}&album=${process.env.IMGUR_ALBUM_DELETE_HASH}&type=url`;
return this.post("upload", formData);
}