Node - POST local file to remote endpoint using AXIOS - node.js

I'm having a bit of trouble taking a local file on the NodeJS server and uploading it to a web endpoint. I keep getting error:
AxiosError: Request failed with status code 400
at settle (D:\myproject\node_modules\axios\dist\node\axios.cjs:1855:12)
at IncomingMessage.handleStreamEnd (D:\myproject\node_modules\axios\dist\node\axios.cjs:2712:11)
at IncomingMessage.emit (node:events:539:35)
at endReadableNT (node:internal/streams/readable:1345:12)
at processTicksAndRejections (node:internal/process/task_queues:83:21) { code: 'ERR_BAD_REQUEST',
I'm not entirely sure what I'm doing wrong. I've poured over a lot of posts and seem to be structuring my AXIOS post request correctly?
try {
const fileStream = await fs.createReadStream('./pathtofile.ext');
let formData = new FormData();
formData.append('myFile', fileStream, 'pathtofile.ext');
axios.post('https://my.endpoint.com', formData, {
headers: {
...formData.getHeaders(),
}
})
.then(res => res.data)
.catch( err => console.log(err))
} catch(err) {
console.error(err)
}
I'm at a bit of a loss, as I can't seem to figure out what I'm doing wrong with such a simple task? The headers also appear to be correct when I call "getHeaders()", it's showing "'content-type': 'multipart/form-data;"
I appreciate any and all assistance!

I'm not sure why you're trying to read the file with a ReadStream, since you need the whole file to post it to your endpoint.
The benefits of using a ReadStream is to perform operations as you read the file. So with every chunk of the file you do something.
In this case, i believe all you need to do is just read the file and then firing your POST request.
You can do this by using readFileSync instead of createReadStream.
Your code should maybe go on this direction:
try {
const fileContents = await fs.readFileSync('./pathtofile.ext',{encoding: 'the-right-encoding'});
let formData = new FormData();
formData.append('myFile', fileContents, 'pathtofile.ext');
axios.post('https://my.endpoint.com', formData, {
headers: {
...formData.getHeaders(),
}
})
.then(res => res.data)
.catch( err => console.log(err));
} catch(err) {
console.error(err)
}
That way, the await keyword would have more sense, since you'll be waiting for the readFileSync promise to resolve.
Be sure to choose the right encoding for reading the file.
Also, since you're using Axios, you can check for the status of the upload by providing a callback function to the post method.
const onUploadProgress = (event) => {
const percentage = Math.round((100 * event.loaded) / event.total);
console.log(percentage);
};
Usage: https://www.bezkoder.com/axios-file-upload/

Firstly, you should go to a site like Reqbin and simulate the request to ensure your web endpoint is not the issue. I took the trouble to test your code with my own endpoint and it works perfectly. Most of the time, such issues are due to Authorization, Rate-limiting or something of that sort. Perhaps I could help out if I knew the kind of endpoint. Good luck.

Related

Node JS v18 Response.formData: undefined when response is multipart

External API responds with a multipart/mixed content type, and in my case, one part is some JSON and the second one is an octet-stream data of a PDF file. I'm building a POC with NodeJS 18 where fetch API is native and expect the response to have formData() method as per specs (https://developer.mozilla.org/en-US/docs/Web/API/Response/formData).
The sample code looks like this (apologies for mixing the promise and await):
fetch(url, {
method: 'GET',
headers,
}).then(async res => {
try {
const parts = await res.formData();
for(const entry of parts.entries()) {
writeFileSync(`./${entry[0]}`, entry[1]);
}
} catch (error) {
console.error('Error:', error);
}
process.exit();
})
.catch(error => {
console.error('Error:', error);
process.exit();
});
what I'm getting is a plain error
Error: TypeError: Response.formData: undefined
Which is quite unexpected. I couldn't find anything related to this behaviour and my first assumption is that the reason is the feature is still experimental.
Another reason I consider is that while API response has a proper Content-Type header it doesn't have Content-Length.
While I can parse the response body using some external dependency I'd rather stick with the native way. Any ideas about what the reason could be for this behavior?

Send blob-data along with a string to backend

I´ve got a weird problem.
Using Node, React, Express, MongoDB -> MERN Stack.
So my page generates a PDF file which then gets send to the backend (as blob data) and is being stored on there.
The problem I have, now I need to send a payment ID along with that blob data to save the order in the data base. I need both in one post request, to make it as smooth as possible:
await axios
.post(process.env.REACT_APP_SERVER_API + '/payment/cash', {
blobData,
paymentId
})
.then(async (res) => ...
like so.
Before, when I just sent the blob data, I could simply access the data in the backend by writing:
exports.createCashOrder = async (req, res) => {
const { filename } = req.file; // THIS RIGHT HERE
const fileHash = await genFileHash(filename);
try {
await saveOrder(filename, fileHash, "cash", paymentId);
//await sendOrderCreatedEmail(req.body, fileHash);
//await sendOrderReceivedConfirmEmail(req.body, fileHash);
res.send({ filename: filename });
}
But that doesn't work anymore. I dont have access to that file object anymore when sending that request object.
Neither by trying
req.body.blobData
req.body.blobData.file
req.file
Any idea how to achieve that, except from making two seperate post requests?
Glad for any help, cheers!
Send the data as a form
await axios
.postForm(process.env.REACT_APP_SERVER_API + '/payment/cash', {
blobData,
paymentId
})
.then(async (res) => ...
And then use multer middleware to handle the form in express.

Next JS, how to send files uploaded to the front end to the back end?

I have a Next application that is a job board. I need to allow the users to upload two files, a resume, and a cover letter. After this, I need to take the files and other information the users provided, then send that information in an email to the employer I have on file for that job posting. The problem I am having is that even though I can get the files and information in a formData object when I send it to my API, I am not able to get the files to use.
This is the method I am using to get the information from the front end and put it into a formData object.
const handleSubmit = async (e) => {
e.preventDefault();
let form = new FormData(e.target);
form.append('resume', resume);
form.append('coverLetter', coverLetter);
const formData = Object.fromEntries(form.entries());
console.log(formData);
try {
const res = await fetch('http://localhost:3000/api/applyForJob', {
body: JSON.stringify(formData),
method: 'POST',
});
const result = await res.json();
console.log(result);
} catch (error) {
console.log('Error with sending the POST request');
console.log('Here is the trace');
console.error(error);
}
};
Here is the formData object in the console:
You can clearly see that all the information is there, including the files. Here is the code for the API route:
export default async function handler(req, res) {
console.log(typeof req.body);
console.log(req.body);
console.log(req.candidateEmail);
console.log(req.resume);
}
I should be able to access everything in the request. But here is what the console logs print:
string
{"candidateFirstName":"Lawrence","candidateLastName":"Mueller","candidateEmail":"lawrencemueller18#gmail.com","candidatePhone":"16266786830","candidateResume":{},"candidateCoverLetter":{},"resume":{},"coverLetter":{}}
undefined
[Function (anonymous)]
The resume and cover letter fields, show {}, so blank. All I need to do is access the files from the front end, just send an email using them. I do not care about storing them at all. Thank you in advance for any help I am given.

JSON array from Express route is undefined in React console

I am currently working on a web app to manage an external database. I am not very familiar with express or NodeJS at this point so I wanted to ask how to send a JSON object to the client sides console without getting undefined?
I have this function to connect then select the what I need and afterwards I converted my JSON object to an array of JSON objects. It displays the data fine in the console as well.
async function connect() {
try {
await sequelize.authenticate();
console.log('Connection has been established successfully.');
} catch (err) {
console.error('Unable to connect to the database:', error);
}
info = await sequelize.query('select * from LeadsInformation', { type: QueryTypes.SELECT });
const details = JSON.stringify(info);
console.log(details);
detailsArray = JSON.parse(details);
console.log(detailsArray);
}
Everything works fine in here, I can get the data and display it in the terminal.
This is my GET route:
app.get("/list", (req, res) => {
connect();
res.json(detailsArray)
});
I have tried a couple of suggested ways based on other explanations and code snippets but none of them has worked so far so I left it like that. I thought foreaching through the data itself in the request would be a solution but it did not work. I also tried using the JSON itself and trying to display it and also tried using the body parser library. Though the library has not been updated for two years. Also I am using axios to fetch the data. It works fine when I try sending a simple string like "hello world" for example.
Is there anything that I'm missing or do you have any other solutions? I would also appreciate an explanation as well if possible.
Edit: It might also have to do something with how I am getting the response in the frontend. I'll look into that as well and will update this thread if I sort it out!
This is the way I get the response. I am currently trying to show in the console. I am using axios API.
Axios({
method: "GET",
url: "http://localhost:5000/list",
headers: {
"Content-Type": "application/json"
}
}).then(res => {
console.log(res.data.json);
});
Probably you have undefined in route because connect function doesn't return anything.
Also connect is an async function it means that it returns Promise and you have to call .then method or use await to get value from it.
Here is the code snippet with fixes that I described above.
async function connect() {
try {
await sequelize.authenticate();
console.log('Connection has been established successfully.');
} catch (err) {
console.error('Unable to connect to the database:', error);
}
info = await sequelize.query('select * from LeadsInformation', { type: QueryTypes.SELECT });
const details = JSON.stringify(info);
detailsArray = JSON.parse(details);
return detailsArray;
}
app.get("/list", async (req, res) => {
const result = await connect();
res.json(result)
});
Notice that in the router handler function I also use async and await because I call connect which is an asynchronous function.
The solution above did work and also another problem I had was that I wasn't getting the response correctly.
I ended up getting the response to the frontend after changing my code to the following from:
console.log(res.data.json);
To:
console.log(res.data[1]);

no_file_data response in slack file upload

I'm using node.js to try to upload a csv file via slackAPI's upload file method. The method is post. I'm unsure how to make this possible because if I use the content argument instead of the file, I get the error:
{ ok: false, error: 'invalid_array_arg' }
If I use the file aargument, I still get the error:
{ ok: false, error: 'invalid_array_arg' }
There are multiple fault points in this code and I've tried to test each one but I'm sure I'm missing some information here. Here's the uploadFile Method that I created:
function uploadFile(file){
console.log(botToken);
axios.post('https://slack.com/api/files.upload', qs.stringify({token: botToken, file: file, channels: 'testing'}))
.then(function (response) {
var serverMessage = response.data;
console.log(serverMessage);
console.log("inside file upload function");
})
}
here's how I call the method:
var file = fs.createReadStream(__dirname + '/' + csvFilePath); // <--make sure this path is correct
console.log(__dirname + '/' + csvFilePath);
uploadFile(file);
And finally the output:
Bot has started!
C:\Users\i502153\WebstormProjects\slackAPIProject/accessLogs/CSV/1548430592860output.csv*
{ ok: false, error: 'invalid_array_arg' }
inside file upload function
What am I doing wrong and how to rectify this?
Links:
https://api.slack.com/methods/files.upload
https://www.npmjs.com/package/axios
Your solution won't work because you are attempting to take a stream object (file) and stringify it into a query string, which is just going to insert the nonsense string "[object]" into the query. It won't actually stream data to Slack.
Axios, unfortunately, doesn't work in node exactly like it does in the browser, and their docs can be a little confusing.
I would suggest an approach like this (untested):
const axios = require('axios');
const FormData = require('form-data');
function uploadFile(file) {
const form = new FormData();
form.append('token', botToken);
form.append('channels, 'testing');
form.append('file', file, 'optionalfilenamehere');
return axios.post('https://slack.com/api/files.upload', form, {
headers: form.getHeaders()
}).then(function (response) {
var serverMessage = response.data;
console.log(serverMessage);
console.log('inside file upload function');
});
}
I adapted this code from the suggestion in ticket https://github.com/axios/axios/issues/1006#issuecomment-320165427, there may be other helpful comments there as well if you run into issues. Good luck!
EDIT: For people reading this later, for a similar approach using request instead of axios, see related question Slack API (files.upload) using NodeJS.

Resources