http request not working in nodejs, causing error - javascript

I am using expressJS on the back end to make a very simple API since I am a beginner. I am sending a request to the back end from the front end and I expect the front end to receive a response. This works fine until I change the nodejs for it to make a second request before sending the original response back to the client. The process looks something like:
Front end sends a POST request
back end receives request, then:
makes its own POST request to a source
waits for this data to come back, then:
sends back a response to the original request from the front end including the data gotten from the second request.
This process works fine when I remove the few lines of code which send the second request, but when the NodeJs back end makes this second request, I get a 404 error returned to the front end - and this error does not come from the second request.
Here is the code:
front end:
function post() {
return new Promise(() => {
$.ajax("URL of my nodejs backend", {
method: "POST",
cache: false,
data: {
action: "test-https"
},
}).then(response => {
console.log(response);
});
});
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.0/jquery.min.js"></script>
nodejs backend (only the bits needed for this question)
const express = require("express");
//const $ = require("./djax.js");
const https = require('https');
const app = express();
app.post("/", (req, res, next) => {
res.header("Access-Control-Allow-Origin", "*");
res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
const body = [];
req.on("data", (chunk) => {
body.push(chunk);
});
req.on("end", () => {
const parsedBody = Buffer.concat(body).toString();
//res.status(200).send("bod" + parsedBody);
// Now parsedBody will be like a query string: key1=val1&key2=val2
const queryObject = new URLSearchParams(parsedBody);
parseRequest(queryObject, res);
//console.log(parsedBody);
});
//console.log(body);
next();
});
function parseRequest(queryParameters, response) {
// Here, queryParameters is a QueryParams object holding the body of the request
// sendResponseFunc is the function which sends back the response for this
// current request.
// Now, we have access to the body of the request and we can use this
// to call the neccessary functions and logic, after which
// send a response back to the front-end via the second
// parameter
const action = queryParameters.get("action");
switch(action.toLowerCase()) {
// ... other cases ...
case "test-https":
sendHttpsRequest(response);
break;
default:
response.status(200).send("Error: unknown action:'" + action.toLowerCase() + "'");
break;
}
}
function sendHttpsRequest(response) {
const postData = JSON.stringify({
works: true
});
const postOpts = {
host: "httpbin.org", // This is a test-server. Not mine.
path: "/post",
method: "POST",
headers: {
"Content-Type": "application/json",
"Content-Length": Buffer.byteLength(postData)
}
};
const newReq = https.request(postOpts, result => {
result.setEncoding("utf8");
res.on("data", chunk => {
console.log("Response" + chunk);
response.status(200).send("Request made from NodeJS end came back " + chunk);
});
});
newReq.write(postData);
newReq.end();
}
When I change the sendHttpsRequest function so that it does NOT make a request, like so:
function sendHttpsRequest(response) {
response.status(200).send("Hi");
}
... then the front-end receives the response and there is no error. There is a 500 internal server error only when the second request is made - when the function sendHttpsRequest is like in the penultimate snippet. I have tried to fix this for two days but I have no idea why this error is happening.
How can I make this second request from the NodeJS server and send back the contents of that without causing the 500 error?

You need to have error handling. You cannot expect that external requests will succeed all the time, for that reason you have to have res.on("error", ...) to respond the client appropriately.
However, I don't see a special case why you are using a data listener to collect payload chunks, it can be simplified very much.
Here is a very simple working example for you
// Backend
const express = require('express');
const axios = require('axios');
const cors = require('cors');
const app = express();
const port = 3000;
app.use(express.json()); // Accepts JSON as a payload
app.use(cors());
app.post('/', (req, res) => {
axios
.get('EXTERNAL URL')
.then((response) => {
console.log('Received payload', req.body);
// Handle response
res.json({data: response.data}).status(201);
})
.catch((error) => {
// Handle error
res.json({
message: error.message,
code: 422})
.status(422);
});
});
app.listen(port, '0.0.0.0', () => console.log(`Started at //127.0.0.1:${port}`));
Required dependencies are
ExpressJS cors package
Axios HTTP client
Axios can be used in browsers as well
Here is your jQuery Ajax request which sends JSON payload instead of FormData
// FE jQuery
function post() {
return new Promise((resolve, reject) => {
$.ajax('http://127.0.0.1:3000', {
contentType: 'application/json; charset=utf-8',
dataType: 'json',
method: 'POST',
data: JSON.stringify({
action: 'test-https',
}),
success: (data) => resolve(data),
error: (err) => reject(err),
});
});
}
post().then(console.log).catch(console.error);

Related

Why is my fetch() sending an empty JSON body?

I've been trying to send a JSON data using fetch but the backend receives an empty object.
In my Client JS I have
const user = "company1";
const username = "muneeb";
const data = {user, username};
fetch("http://127.0.0.1:3000/users/api/login", {
method: 'POST',
body: JSON.stringify(data)
}).then((response) => {
console.log(response);
});
The server side has:
router.post('/users/api/login', async (req, res, next) => {
try {
// console.log(request.body);
const request = JSON.stringify(req.body);
let imageTitles = [];
console.log(request);
*its random from here on out but you get the idea*
await components.getImages(imageTitles);
const finalKey = imageTitles.join("");
let images = await components.output(req.body.user ,req.body.username);
res.send(components.jsonConverter(imageTitles, images)); //---Top priority
db.setPassword(req.body.user, req.body.username , finalKey);
} catch (err) {
console.log(err);
res.send(err).sendStatus(500);
};
})
A few things I have already tried :
It works perfectly in Insomnia(postman).
express.json() is present , it helped me go from undefined to blank JSON.
I have enabled cors settings.
That's it for now.
The body parser express.json will only be applied for requests with Content-Type: application/json. You have to add the content type to your fetch call:
fetch("http://127.0.0.1:3000/users/api/login", {
method: 'POST',
body: JSON.stringify(data),
headers: {
'Content-Type': 'application/json'
}
})

multiple asynchronous post request with files in nodejs

I've tried to send a bunch of post requests with file upload in nodejs.
using axios.post, I could make a single request easily. But I got an error when trying send multiple asynchronous requests.
Based on the axios document, it uses axios.all([ axios.get(), axios.get(), ...]) to make async requests at time.
If I sent my code, the error says:
"Error: Request failed with status code 500 ~ "
. This error is the same when I send a request without file upload. So I guess my code doesn't attach a file when I send async request.
Please advise me what I am missing.
My code is below:
var axios = require('axios');
var FormData = require('form-data');
var fs = require('fs');
var data = new FormData();
data.append('files', fs.createReadStream('./liscense.jpg'));
var config = {
method: 'post',
url: 'https://domainname/scan/id',
headers: {
...data.getHeaders()
},
data : data
};
axios
.all([axios(config), axios(config)])
.then(
axios.spread((res1, res2) => {
console.log(res1.data, res2.data);
})
)
.catch((error) => {
console.log(error);
});
Your problem is that you are sending a empty stream,
There is an "_streams" array in your form data that contains the stream of your "liscense.jpg" file, and when you POST the first request to your target host, this stream will be empty and the stream of your other requests is empty, so the file does not reach your destination.
In short, this code only send your file once in first request, and other requests do not include your file/files.
you can try this:
const axios = require('axios');
const FormData = require('form-data');
const fs = require('fs');
function postRequest() {
const data = new FormData();
data.append('files', fs.createReadStream('./liscense.jpg'));
const config = {
method: 'post',
url: 'https://domainname/scan/id',
headers: {
...data.getHeaders()
},
data: data
};
return config;
}
axios
.all([axios(postRequest()), axios(postRequest())])
.then(
axios.spread((res1, res2) => {
console.log(res1.data, res2.data);
})
)
.catch((error) => {
console.log(error);
});

Javascript fetch(POST) to express server fails. The server does not receive the request from JS, but receives request from Postman

MRE -> node-server : react app
When I send a POST request using Postman, I get the expected result. This is the request that I am sending using Postman
and test sent gets printed to the console of my node server
If I send a request from my react form however, test sent does not print to the console, but the catch block of my fetch request get's executed and err is printed to the console of my react app, followed by {}.
I would like to know why my POST request is not working and is not getting received by the server
Below is the function that I call when someone clicks the submission button of my form created in react
Function called on form submission
nodeUrl = 'https://localhost:6060?'
const submitData = async () => {
fetch(nodeUrl, {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({'test': 'test'})
}).then((res) => {
alert('then')
}).catch((err) => {
alert('err')
alert(JSON.stringify(err))
})
}
}
This is the server that I run using node server.js
server.js
server.post('/', function(req, res) {
console.log('test sent')
mailer.messages().send(req.body)
.then((mes) => {
console.log(mes)
res.json({ message: 'Thanks for your message. Our service team has been notified and will get back to you shortly.' })
}).catch(err => {
console.log(err)
res.json(err);
})
});
The majour issue here is due to CORS. CORS support can be used to overcome this. Just keep in mind to have this only for development mode(see below codes).
But, as per the Postman's snapshot and provided GitHub repositories, the request from Front-end should be of multipart/form-data type. Thus, the Front-end code would look like this
const nodeUrl = "http://localhost:6060/";
const submitData = async () => {
// create a FormData object
const formData = new FormData();
formData.append('form', 'example#email.com');
formData.append('to', 'example#email.com');
// this auto adds 'multipart/form-data' + HASH header in the request
fetch(nodeUrl, {
method: "POST",
body: formData
})
.then(res => {
console.log(res);
}).catch(err => {
console.log('Error -', err);
});
};
To handle multipart/form-data request in the ExpressJS, you need a plugin Multer.
const express = require('express');
const bodyParser = require('body-parser');
const multer = require('multer'); // for 'multipart' type request
const server = express();
const upload = multer();
// allow CORS requests in development mode
if (process.env.NODE_ENV === 'development') {
// Server run command - "NODE_ENV=development node server.js"
const cors = require('cors');
server.use(cors());
}
server.use(bodyParser.json());
server.use(bodyParser.urlencoded({extended: true}));
// using Multer middleware form extracting 'FormData' payload
server.post('/', upload.none(), function(req, res) {
console.log('Received body', req.body);
... // other codes
});
Strategy 2(plain JSON) -
If that 'multipart/form-data' strategy was unintentional and you just want to send simple JSON, use below codes -
In Front-end, trigger API request as -
fetch(nodeUrl, {
method: "POST",
headers: {
'Content-Type': 'application/json', // this needs to be defined
},
body: JSON.stringify({ from: 'some#email.com', to: 'other#email.com' })
})
In server, just ignore codes related to Multer and only keep your API as -
server.post('/', function(req, res) {
console.log('Received body', req.body);
... // other codes
});
I ended up using a better fetch request, which was put together for me by selecting code -> Javascript Fetch in Postman(under the save button)
var myHeaders = new Headers();
myHeaders.append("Content-Type", "application/x-www-form-urlencoded");
var urlencoded = new URLSearchParams();
urlencoded.append("from", "example#email.com");
urlencoded.append("test", "test");
var requestOptions = {
method: 'POST',
headers: myHeaders,
body: urlencoded,
redirect: 'follow'
};
fetch("http:localhost:6060/, requestOptions)
.then(response => {
if (response.ok){
response.json().then(json => {
console.log(json)
})
}
})
.catch(error => console.log('error: ', error))

How to handle client POST body request flow using Expect: 100-continue header?

I am an impatient learner
I'm looking for information on how to control data flow between client and server in this way:
what I want is, the client sends a POST request, together with an Expect: 100-continue header, then the server processes the headers and validates session information, if everything is ok the server sends the response status code 100 and finally the client sends the body of the request.
my doubts are not about the validation of the headers but about how to regulate the dataflow of the client request POST body to server, if validation result is not the expected reject request and respond client request error status
if is there any way to do this, how to do it correctly?
I don't speak english natively apologies for any mistake
thanks for any help.
Here an example with node 12:
// server.js
const http = require('http')
// This is executed only when the client send a request without the `Expect` header or when we run `server.emit('request', req, res)`
const server = http.createServer((req, res) => {
console.log('Handler')
var received = 0
req.on('data', (chunk) => { received += chunk.length })
req.on('end', () => {
res.writeHead(200, { 'Content-Type': 'text/plain' })
res.write('Received ' + received)
res.end()
})
})
// this is emitted whenever the client send the `Expect` header
server.on('checkContinue', (req, res) => {
console.log('checkContinue')
// do validation
if (Math.random() <= 0.4) { // lucky
res.writeHead(400, { 'Content-Type': 'text/plain' })
res.end('oooops')
} else {
res.writeContinue()
server.emit('request', req, res)
}
})
server.listen(3000, '127.0.0.1', () => {
const address = server.address().address
const port = server.address().port
console.log(`Started http://${address}:${port}`)
})
The client
// client.js
var http = require('http')
var options = {
host: 'localhost',
port: 3000,
path: '/',
method: 'POST',
headers: { Expect: '100-continue' }
}
const req = http.request(options, (response) => {
var str = ''
response.on('data', function (chunk) {
str += chunk
})
response.on('end', function () {
console.log('End: ' + str)
})
})
// event received when the server executes `res.writeContinue()`
req.on('continue', function () {
console.log('continue')
req.write('hello'.repeat(10000))
req.end()
})

Sending JSON response from NodeJS/Express

sorry for the n00b question I have been kinda stuck so I was hoping you guys could put me in the right direction.
I am making an app that is retrieving data by NODEJS from a REST API. (This is a success and works).
I then have a listen URL (my own API) in express that I invoke by going to the browser http://localhost/api or by using POSTMAN. So far so good, I see in the console (NODE Console) that my request gets handled perfectly as I see the JSON response, however, I would also like to see the JSON response in the browser or POSTMAN as JSON Response, not just the console I know I am missing something in my (simple) code but I am just starting out.... Please help me out here is my code.
var express = require("express");
var app = express();
const request = require('request');
const options = {
url: 'https://jsonplaceholder.typicode.com/posts',
method: 'GET',
headers: {
'Accept': 'application/json',
'Accept-Charset': 'utf-8',
}
};
app.get("/api", function(req, res) {
request(options, function(err, res, body) {
var json = JSON.parse(body);
console.log(json);
});
res.send(request.json)
});
app.listen(3000, function() {
console.log("My API is running...");
});
module.exports = app;
Much appreciated!
To send json response from express server to the frontend use res.json(request.json) instead of res.send(request.json).
app.get("/api", function(req, res) {
request(options, function(err, res, body) {
var json = JSON.parse(body);
console.log(json); // Logging the output within the request function
}); //closing the request function
res.send(request.json) //then returning the response.. The request.json is empty over here
});
Try doing this
app.get("/api", function(req, res) {
request(options, function(err, response, body) {
var json = JSON.parse(body);
console.log(json); // Logging the output within the request function
res.json(request.json) //then returning the response.. The request.json is empty over here
}); //closing the request function
});
Much thanks to ProgXx, turned out I used the same res and response names. Here is the final code. Much thanks ProgXx
var express = require("express");
var app = express();
const request = require('request');
const options = {
url: 'https://jsonplaceholder.typicode.com/posts',
method: 'GET',
headers: {
'Accept': 'application/json',
'Accept-Charset': 'utf-8',
'User-Agent': 'my-reddit-client'
}
};
app.get("/api", function(req, res) {
request(options, function(err, output, body) {
var json = JSON.parse(body);
console.log(json); // Logging the output within the request function
res.json(json) //then returning the response.. The request.json is empty over here
}); //closing the request function
});
app.listen(3000, function() {
console.log("My API is running...");
});
module.exports = app;

Categories

Resources