Node Function Running Twice - javascript

I am using a simple JS script to query a Postgres DB. I simply want to write a simple query to the webpage, but every time I think I'm calling the function, it calls twice
// query the db
rate = () => pool
.query(rateQuery)
.then(res => {return res.rows[0]})
.catch(err => console.log('error: ', err.stack))
const app = http.createServer((request, response) => {
// set response header
response.writeHead(200, { 'Content-Type': 'text/html' });
// get result of promise
r = rate()
.then(res => response.write(JSON.stringify(res), () => {console.log("DONE"); response.end()}))
.catch(err => console.log('error: ', err.stack))
});
app.listen(3000);
When the page is refreshed, it prints DONE twice but I only want it once, any help is much appreciated - thanks.

If you add console.log(request.url) in your request handler, you will likely see that that the second request is when the browser asks your server for the /favicon.ico that represents the site. Browsers do this. When a user types in a URL for a site and hits enter, the browser requests that URL from the target site and then it also asks for /favicon.ico if there wasn't a previously cached icon already for that site.
In general, you should not have an http request handler like this that pays no attention to the request URL path because then you will process anything that the browser or a search crawler or anything sends you. Instead, you should look for a specific URL path and only do your work when it's the desired path and return a 404 response for all other paths.
I'd suggest you change your request handler to this:
const app = http.createServer((request, response) => {
// set response header
if (request.url === "/") {
// get result of promise
rate().then(res => {
response.writeHead(200, { 'Content-Type': 'text/plain' });
response.write(JSON.stringify(res), () => {
response.end();
console.log("DONE");
});
}).catch(err => {
console.log('error: ', err.stack);
response.writeHead(500);
response.end();
});
} else {
response.writeHead(404, "unknown URL");
response.end();
}
});
Note: I change the content-type to text/plain. You had it as text/html, but you weren't sending html at all. You could perhaps make the content-type application/json, but in any case when you're sending JSON, it's not HTML.
This also incorporates cleaner error handling where all error paths send an error response.
P.S. Writing out this simple request handler using the plain http.createServer() request handler reminds me of how much simpler it is to use Express for request handler implementations. It handles so much of this for you while still giving you full control. It's lightweight and simpler.
FYI, here's the program written using Express:
const express = require('express');
const app = express();
app.get("/", (req, res) => {
rate().then(res => {
res.json(res);
}).catch(err => {
console.log(err);
res.sendStatus(500);
});
});
// if we get here, then no other request handler handled the incoming request
// so we send back a 404
app.use((req, res) => {
res.sendStatus(404);
});
app.listen(3000);

There is one possibility. rate already is a function which you defined as
// query the db
rate = () => pool
.query(..........
Therefore, I don't think you would need parenthesis when you call it. Change from this
// get result of promise
r = rate()
.then .........
to this
// get result of promise
r = rate
.then .........

Related

How do you restart a streaming HTTP request on an error

I am accessing an API that streams (RFC2616 for HTTP/1.1) the response. The stream never ends, but from time to time it throughs an error requiring the program to restart the request. I can't figure out how in NodeJS to restart the request without restarting the program. I've tried loops to check if an error occurred, but I end up creating a blocking code that stops the request methods. The code below works until an error occurs.
const request = require('request');
const options = {
method: 'GET',
url: 'https://api.tradestation.com/v3/marketdata/stream/quotes/IWM',
headers: {Authorization: 'Bearer ' + api.token,},
};
request(options)
.on('data', function (data) {
console.log(data.toString());
//Process data here
})
.on('error', function (err) {
console.error(err);
});
If it helps, the documentation to the API for streaming is here, and the API call is here.
On a side note, I tried using Axios but it doesn't seem to like endless streams (tried onDownloadProgress).
I would stick your request inside a function, you can then call it again on error.. I would also put a slight delay on the retry..
eg.
function callAPI() {
request(options)
.on('data', function (data) {
console.log(data.toString());
//Process data here
})
.on('error', function (err) {
console.error(err);
setTimeout(callAPI, 1000); //wait a second and retry.
});
}

How can I get a value in header with nodejs and express?

I need to get the value x-api-key that send me in the header in my end point with method get. the code in my end point is the next.in my localhost work fine. but in the server not work. the error is
Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent
to the client
router.get('/orders',async (req, res, next) => {
const apikey = req.header('x-api-key')
try {
const customer = await Customer.find({apikey:apikey})
const orders = await Order.find({ customer: ObjectId(customer[0]._id)})
res.status(200).json(orders)
} catch (error) {
res.status(400).json({"message":"Error"})
next(error)
}
})
I've seen other posible solutions but I cant find the solution.
One cause of that problem is this:
res.status(400).json({"message":"Error"})
next(error)
You want to pick only one of those two options for sending the error response as they both try to send a response, thus you are trying to send two responses to the same request and you get the error you report.
When you call next(error) that will end up in your generic Express error handler (if you installed one or the default error handler if you did not) and will send an error response.
If you just want to send the error response here and be done with the request, then just remove the next(error). That should only be there if you want the general Express error handler to send the error response.
FYI, you will generally want to always log the actual error on your server so if this starts happening on your server, you can look at the logs and see what the lower level error is.
router.get('/orders',async (req, res, next) => {
const apikey = req.header('x-api-key');
try {
const customer = await Customer.find({apikey:apikey});
const orders = await Order.find({ customer: ObjectId(customer[0]._id)});
res.status(200).json(orders)
} catch (error) {
console.log(error);
res.status(400).json({"message":"Error"});
}
})
Error [ERR_HTTP_HEADERS_SENT]
According to another stackoverflow answer:
That particular error occurs whenever you try to send more than one response to the same request and is usually caused by improper asynchronous code.
Therefore, the point of error is most likely the next(error) part
router.get('/orders',async (req, res, next) => {
const apikey = req.header('x-api-key')
try {
const customer = await Customer.find({apikey:apikey})
const orders = await Order.find({ customer: ObjectId(customer[0]._id)})
res.status(200).json(orders)
} catch (error) {
res.status(400).json({"message":"Error"})
try{next(error)} //this is most likely the source of your error
catch(err){console.log(err)}
}
})

Stop execution after writing to text file with fs.writeFile

I have the following Node.JS (ran with Express) code :
let app = express();
app.use(cors());
app.get('/callback', function (req, res) {
// your application requests refresh and access tokens
// after checking the state parameter
var code = req.query.code || null;
var authOptions = {
url: 'https://accounts.spotify.com/api/token',
form: {
code: code,
redirect_uri: redirectUri,
grant_type: 'authorization_code'
},
headers: {
'Authorization': 'Basic ' + (new Buffer(clientId + ':' + clientSecret).toString('base64'))
},
json: true
};
request.post(authOptions, function (error, response, body) {
if (!error && response.statusCode === 200) {
var access_token = body.access_token,
refresh_token = body.refresh_token;
fs.writeFile('test.txt', 'HELLO', function (err) {
if (err) return console.log(err);
console.log('Hello World > helloworld.txt');
});
}
}
)
});
console.log('Listening on 8888');
app.listen(8888);
The route is used as a callback for a request to the Spotify Web API, thus I can get an access token.
Spotify then redirects to the callback function above, you can see it in the URI by looking at "redirect_uri".
If you need more information about the Spotify Authorization Flow, see here.
Here's the URI I'm using to authenticate my app to Spotify.
https://accounts.spotify.com/authorize?client_id=CLIENT_ID&response_type=code&redirect_uri=http://localhost:8888/callback&scope=user-read-private%20user-read-email%20playlist-modify-public&state=PexBrjEzISHepTp7&show_dialog=false
CLIENT_ID is replaced by my real CLIENT_ID in the request I make
My problem is located to the file writing part :
fs.writeFile('test.txt', 'HELLO', function (err) {
if (err) return console.log(err);
console.log('Hello World > helloworld.txt');
});
When the callback route is called by Spotify, I have the string "HELLO" wrote in my text file, so the file writing is functional.
But even if it has finished writing the string, the Chrome page is still running and "pending" on the server. It runs for a few minutes and then crash by saying that the page didn't sent any data. Why ?
I've looked at this page talking about the methods of writing to text files, using writeFile and writeFileAsync, but using both of them didn't solved my problem.
EDIT: I don't really want to stop the Express process! I just want to be able to process another request :)
Any idea ? Thanks in advance :)
You aren't returning anything from your route, try adding res.send({})
In your get route you are not sending response, you must send response irrespective of writing a file was successful or not.
Add below code post writing to file (as well as in if error case)
res.send({YOUR_CHOICE_RESPONSE_DATA})

Puppeteer , listen to network response changes

I am using Puppeteer api (https://github.com/GoogleChrome/puppeteer)
For automation testing.
I want to listen to all http response, need the url and the response data for each one.
I try to so use the page.on('response') function :
page.on('response', response => {
response.text().then( textBody=> {
const req = response.request();;
console.log(req.url())
console.log(textBody)
});
})
Should warp in 'waitForSelector' function , to have a flag that the data is ready?
I try to do so.
The problem is some time i do not see any console.log and some time i do.
I will glad to know what do i do wrong?
There is no need to call response.request(), unless you are trying to obtain the URL of the matching request object.
The following solution will work just fine:
page.on('response', response => {
console.log('Response URL:', response.url());
response.text().then(data => {
console.log('Response Text:');
console.log(data);
});
});
If you are still having issues, it could be because the associated request has failed.
You can check for this error by listening for the requestfailed event and logging the result:
page.on('requestfailed', request => {
console.log('Failed Request URL:', request.url());
console.log('Failed Request Error Message:', request.failure().errorText);
});

Dialogflow Webhook (Webhook call failed. Error: 500 Internal Server Error)

I've followed this tutorial's code (https://dialogflow.com/docs/getting-started/basic-fulfillment-conversation) to return results of an API to dialog flow. However my webhook keeps failing. Can someone help me figure out why?
Here's one of the failed conversations:
Here's my code:
'use strict';
const http = require('http');
exports.Hadoop = (req, res) => {
// Get name node server from the request
let nameNodeServer = req.body.queryResult.parameters['nameNodeServer']; // nameNodeServer is a required param
// Call the Hadoop API
getNameNodeInfo(nameNodeServer).then(function(output) {
res.json({ 'fulfillmentText': output }); // Return the results to Dialogflow
}).catch(() => {
res.json({ 'fulfillmentText': 'getNameNodeInfo() Error'- });
});
};
function getNameNodeInfo (nameNodeServer) {
return new Promise((resolve, reject) => {
// Create url for the HTTP request to get the name node info
let url = 'http://' + nameNodeServer + '[rest of url]';
// Make the HTTP request to get the name node info
http.get(url, (res) => {
let body = ''; // var to store the response chunks
res.on('data', (chunk) => {body += chunk; });
res.on('end', () => {
// After all the data has been received, parse the JSON for desired data
let response = JSON.parse(body);
let beans = response['beans'][0];
// Create response
let output = `Percent Used: ${beans['PercentUsed']}`;
// Resolve the promise with the output text
console.log(output);
resolve(output);
});
res.on('error', (error) => {
console.log(`Error calling the Hadoop API: ${error}`);
reject();
});
});
});
}
I believe the getNameNodeInfo function and the retrieval of the name node server are correct, as they logged the correct output in debugging.
Diagnostic Info:
I contacted someone at Dialogflow and this was their response.
Thank you for providing all the information. I have observed in your
code that you have used http requests instead of https. The service
must use HTTPS and the URL must be publicly accessible in order for
the fulfillment to function. Dialogflow does not support self-signed
SSL certs. For information on SSL setup, please refer to this :
https://developers.google.com/web/fundamentals/security/encrypt-in-transit/enable-https
We've had a somewhat different, but related, issue:
Internal Server Error when running an agent.
“status”: {
“code”: 500,
“errorType”: “internal_server_error”,
“errorDetails”: “Internal Server Error”
},
This error was not caused by any changes we introduced. We are using that agent in a dev version of an app and one morning it stopped working.
We tested by creating a .zip and restoring into a new agent. The new agent would work properly, but we would continue to get the 500 error on the agent hooked into our dev app. We submitted a help request and overnight the error got resolved. We suspect that DialogFlow team had to manually reboot the server or something similar.

Categories

Resources