How to check states after sending request in server? - node.js

I am trying to check states after sending requests to the server by using axios. I designed the server that if you submitted the form with an empty input, you will get an error. If you can see in the code, I have tried to check the states in finally block but it is not working properly. Like when I submitted the form initially with no inputs, the console log displays no errors and when I try to submit the form with the inputs, it doesn't display anything in the console. I just want to check if there is an error with the request because I want to run a function between them.
The server I used is live and running and you can get the data/submitted form by changing the URL into /getUser
Code here: https://codesandbox.io/s/quizzical-danny-dv1l7?file=/src/App.js

It doesnt works like that.
const [error, setError] = useState("");
error is the initial value (empty string). In dont realy knows how useState is working but, error is a string so it s a value, not a reference. There is no way this variable get updated it the finaly block.

The simple answer is you are setting your state inside the function and then trying to read it as your "current state". Try this instead...
const handleSubmit = async (e) => {
e.preventDefault();
try {
await axios.post("https://testing-name-app.herokuapp.com/create", {
first,
last
});
} catch (error) {
console.log("error found");
return setError(error.response.data.errorMessage);
}
// be CAREFUL with this pattern! This just means the request came back
// with no errors, but there may be a message from your call that
// contained an error from your db/server etc
return console.log("no errors");
};
And here's a way to quickly see what's going on in your call...
const handleSubmit = async (e) => {
e.preventDefault();
let res
try {
res = await axios.post("https://testing-name-app.herokuapp.com/create", {
first,
last
});
} catch (error) {
res = error
console.log("error found");
setError(error.response.data.errorMessage);
}
finally {console.log('res: ', res)}
};

Related

Returning a value from a mix of async/sync function from a provider (different script) to express server

Apologies for asking this question - I know there are tons of information about async functions out there but I seem to have tried everything and cannot find a solution..
First of all let me outline the architecture of my program. There are two scripts: a main server script (node.js, express), which processes GET requests and provider script, which deals with the blockchain in the background to return some values. The server script is responsible for invoking a method that returns a value from the provider. The provider does all the work.
The snippet of the provider script:
getInfo(index, account, key) {
//Waiting on an asynchronous method, which does some work in the blockchain in the background; everything functions as it should be
try {
await this.getBlockchain
(
index
, account
, key
).then(result => {
// Here instead I invoke a SYNCHRONOUS method, which simply formats the response in a correct way
const reply = this.sendReply(result)
console.log(reply) //Logs the correct reply in the format in which the server is expecting it
return reply;
});
}
catch (error) {
return { error: 003, result: false };
}
}
The snippet of the server script:
server.get("/getAccount", async (req, res) => {
let index = req.query.index;
let account = req.query.account;
let key = req.query.key;
// Here I also check for the validity of the query values, irrelevant to this issue
// The provider class is imported as provider, hence, the provider.method (this has been tested many times before)
try {
await provider.getInfo(index, account, key).then(reply => {
const { error: infoError, result: infoValue } = reply
if (infoError == false) {
res.send(`${infoValue}`);
} else {
res.send(`${infoError}`);
};
});
}
catch (error) {
res.send("008");
}
}
);
I honestly have no idea how to approach this; I tried self-contained async function on the server side as well as different syntax but the reply is always undefined even though the reply from a synchronous call in the provider is correct.
Could someone help me to understand what I'm doing wrong? This is my first time working with async with numerous scripts and functions and I'm finding it very confusing.
Thank you so much!
With your current structure, you need to return the result of the await so that the top level of your function is returning something from the async function.
async getInfo(index, account, key) {
try {
let retVal = await this.getBlockchain(index, account, key).then(result => {
return this.sendReply(result);
});
return retVal;
} catch (error) {
return { error: 003, result: false };
}
}
But, really, it's a better coding style to not mix await and .then() and to just go with one style like this:
async getInfo(index, account, key) {
try {
let result = await this.getBlockchain(index, account, key);
return this.sendReply(result);
} catch (error) {
return { error: 003, result: false };
}
}
Note, this function never rejects because it's catching its own rejections and turning it into a resolved value. So, the caller cannot use .catch() to see errors. The caller must always check for the error property in the resolved object. This is not usually how you program with promises. It can be made to work, but often does not meet the expectations of the caller (as errors are usually communicated back via rejected promises).
This has to be a dup. but... Don't mix await and .then.
You simply try/catch around await.
try {
const reply = await provider.getInfo(index, account, key);
const { error: infoError, result: infoValue } = reply
if (infoError == false) {
res.send(`${infoValue}`);
} else {
res.send(`${infoError}`);
};
} catch (error) {
res.send(500);
}

what is wrong in this section

app.get('/dir/:dirname', (req, res) => {
const isFile = fileName => {
return fs.lstatSync(fileName).isFile();
}
var retString = '';
var dir = `d:\\${req.params.dirname}`;
console.log(dir);
retString+='<table>';
fs.readdirSync(dir).map(fileName => {
console.log(fileName);
//retString+=`<tr><td>${dir}</td><td><a href='${path.join(dir, fileName)}>${fileName}</a></td></tr>`;
retString+=`<tr><td>${dir}</td><td>${fileName}</td></tr>`;
}).filter(isFile);
retString += '</table>';
res.send(retString);
res.end();
});
it delivers the file names, but runs into error after the end of the list.
What did I miss out?
Your .map() was not returning anything from the callback. That means you pass an array of undefined to .filter() which then tries to pass undefined to fs.lstatSync() which causes your error.
You don't call both res.send() and res.end() because res.send() already ends the response so when you then call res.end() again, that can cause an error.
res.end() is used when you are using res.write() which can be called multiple times and does not end the response.
Also, your .filter(isFile) is not doing anything useful. You're building the HTML before you filter and then not saving of using the result of the filter. You need to filter before you map as in:
fs.readdirSync(dir).filter(isFile).map(...)
Here's how your code looks using asynchronous file I/O and using the withFileTypes option and inserting some error handling:
app.get('/dir/:dirname', async (req, res) => {
try {
let retString = '';
// this is potentially dangerous as ANY user of this server
// can browse anywhere on your d: drive
const dir = `d:\\${req.params.dirname}`;
console.log(dir);
retString += '<table>';
let entries = await fs.promises.readdir(dir, { withFileTypes: true });
for (let entry of entries) {
if (entry.isFile()) {
console.log(entry.name);
retString += `<tr><td>${dir}</td><td>${entry.name}</td></tr>`;
}
}
retString += '</table>';
res.send(retString);
} catch (e) {
console.log(e);
res.sendStatus(500);
}
});
Other Issues:
This is potentially dangerous code as it allows anyone with access to your server to browse anywhere they want on your d: drive with no restrictions
This presumably should return a fully formed web page, not just an HTML table.
Thank you jfriend - this did it.
I am beginning to understand node.js
Dont worry about security - this is running on local network only and will be limited before getting to be used elsewere.
I am aware of this leak.
But thank you for this hint as well, because others might need this reminder.

Throwing exceptions on AWS Lambda with Node12.x in Axios catch handlers

I'm having a hard time trying to understand how I can make this function work. I think it stems from me not really understanding 100% how to handle promises, but I've read all the tutorials I can find and I still don't see how I can do this.
The following is a function, which calls an endpoint on an API. When it's successful, it'll return a token for use in future requests. When it fails, I want to throw an exception and completely stop the execution.
I've heard that when throwing in a catch handler like this, then you're thrown exception is handled by a hidden try/catch and this passes to the next catch handler. But this is effectively meaning my exception gets black-holed. Which I've heard is a thing and I've read other questions on this website about that.
The actual example isn't this trivial. But the effective code is still the same, a try/catch wrapped around all the code in question. But any exceptions never land in the right place. Then lambda fails with the exception in question. But it's not handled by the code that I wrote, so the response is wrong. I need to handle the code and output a different reply based on what exception is being thrown. So I change it around, fix up the data, add other related information and return that. But since I'm never landing in the right place. I can never do that.
How could I alter this function, so that it correctly lands in the exception handler I've given? Thanks for your help
let getToken = async () => {
let transport = axios.create({ baseURL, headers: passwordHeaders });
return transport.post('/authorization/login')
.then(response => {
let accessToken = _.get(response, 'data.access_token', null);
if(accessToken !== null){
let base64 = Buffer.from(accessToken, 'base64');
let token = base64.toString('ascii');
return token.split(";").shift().split(":").pop();
}
throw new LoginError(response.statusText, response.status));
})
.catch(error => {
if(_.get(error, 'response.status', 'null') === 403){
throw new UnauthorizedError(error.response.statusText);
}
throw error;
});
}
try{
let token = await getToken();
}catch(error){
console.log("this exception should land here");
}
As mentioned in the problems the main problem is the mix of then style and async/await. If we lean into the async approach we could do the following:
let getToken = async () => {
let transport = axios.create({ baseURL });
try {
const response = await transport.post("/authorization/login");
let accessToken = _.get(response, "data.access_token", null);
if (accessToken !== null) {
let base64 = Buffer.from(accessToken, "base64");
let token = base64.toString("ascii");
return token.split(";").shift().split(":").pop();
}
} catch (e) {
throw e;
}
};
try {
let token = await getToken();
} catch (error) {
console.log("this exception should land here");
}
As getToken is an asynchronous function it returns a Promise, returning within this function equals to using Promise.resolve and throwing equals to Promise.reject. In my case the catch block of getToken does not do anything and getToken gets called from within a try / catch. We might want to delegate error handling to the outer try catch by removing the inner one.

How to I extract the contents of a variable and place them into a constant? Node.js

Im trying to extract the contents of variable topPost and place it into const options under url. I cant seem to get it to work. Im using the snoowrap/Reddit API and image-downloader.
var subReddit = r.getSubreddit('dankmemes');
var topPost = subReddit.getTop({time: 'hour' , limit: 1,}).map(post => post.url).then(console.log);
var postTitle = subReddit.getTop({time: 'hour' , limit: 1 }).map(post => post.title).then(console.log);
const options = {
url: topPost,
dest: './dank_memes/photo.jpg'
}
async function downloadIMG() {
try {
const { filename, image } = await download.image(options)
console.log(filename) // => /path/to/dest/image.jpg
} catch (e) {
console.error(e)
}
}
the recommended formatting for the image downloader is as follows:
const options = {
url: 'http://someurl.com/image.jpg',
dest: '/path/to/dest'
}
async function downloadIMG() {
try {
const { filename, image } = await download.image(options)
console.log(filename) // => /path/to/dest/image.jpg
} catch (e) {
console.error(e)
}
}
downloadIMG()
so it looks like i have to have my url formatted in between ' ' but i have no idea how to get the url from var topPost and place it in between those quotes.
any ideas would be greatly appreciated.
Thanks!
topPost is a Promise, not the final value.
Promises existence is to work with asynchronous data easily. Asynchronous data is data that returns at a point in the future, not instantly, and that's why they have a then method. When a Promise resolves to a value, the then callback is called.
In this case, the library will connect to Reddit and download data from it, which is not something that can done instantly, so the code will continue running and later will call the then callback, when the data has finished downloading. So:
var subReddit = r.getSubreddit('dankmemes');
// First we get the top posts, and register a "then" callback to receive all these posts
subReddit.getTop({time: 'hour' , limit: 1,}).map(post => post.url).then((topPost) => {
// When we got the top posts, we connect again to Reddit to get the top posts title.
subReddit.getTop({time: 'hour' , limit: 1 }).map(post => post.title).then((postTitle) => {
// Here you have both topPost and postTitle (which will be both arrays. You must access the first element)
console.log("This console.log will be called last");
});
});
// The script will continue running at this point, but the script is still connecting to Reddit and downloading the data
console.log("This console.log will be called first");
With this code you have a problem. You first connect to Reddit to get the top post URL, and then you connect to Reddit again to get the post Title. Is like pressing F5 in between. Simply think that if a new post is added between those queries, you will get the wrong title (and also you are consuming double bandwidth consumption, which is not optimal too). The correct way of doing this is to get both the title and the url on the same query. How to do so?, like this:
var subReddit = r.getSubreddit('dankmemes');
// We get the top posts, and map BOTH the url and title
subReddit.getTop({time: 'hour' , limit: 1,}).map(post => {
return {
url: post.url,
title: post.title
};
}).then((topPostUrlAndTitle) => {
// Here you have topPostUrlAndTitle[0].url and topPostUrlAndTitle[0].title
// Note how topPostUrlAndTitle is an array, as you are actually asking for "all top posts" although you are limiting to only one.
});
BUT this is also weird to do. Why don't you just get the post data directly? Like so:
var subReddit = r.getSubreddit('dankmemes');
// We get the top posts
subReddit.getTop({time: 'hour' , limit: 1,}).then((posts) => {
// Here you have posts[0].url and posts[0].title
});
There's a way to get rid of JavaScript callback hell with async/await, but I'm not going to enter into matter because for a newbie is a bit difficult to explain why is not synchronous code although it seems to look like so.

Creating promises in loop

I have to create promises in loop according to given config file and return response when all are resolved. Here goes the code-
{for(let type in spotlight){
switch (type){
case "outliers":{
let ops= spotlight[type];
for(let i=0;i<ops.length;i++){
(function(op){
let p= new Promise(function(resolve,reject){
let reqUrl= urlCreator(op.uri,op.query);
//console.log("--------------------"+reqUrl);
apiService.get(reqUrl,function(isSuccess,data){
if(!isSuccess){
return reject(data);
}
// console.log(isSuccess);
// console.log(data);
// console.log("trend is ------"+JSON.stringify(op));
// create objects array
// let temp= [];
// let overallScore= data.overall.score;
// for(let day in overallScore){
// temp.push({"key": day,"value": parseFloat(overallScore[day])});
// }
//let outliers= stats.outliers(temp,"key","value");
resolve({"type":type,"name": op.name,"data": outliers});
})
});
promiseArray.push(p);
}(ops[i]))
}
break;
}
case "filters":{
let ops= spotlight[type];
for(let i=0;i<ops.length;i++){
(function(op){
let p= new Promise(function(resolve,reject){
let reqUrl= urlCreator(op.uri,op.query);
apiService.get(reqUrl,function(isSuccess,data){
if(!isSuccess){
return reject(data);
}
// console.log(isSuccess);
// console.log(data);
// console.log("coc is ------"+JSON.stringify(op));
resolve({"type": type,"name": op.name,"data": data});
})
})
promiseArray.push(p);
}(ops[i]))
}
break;
}
}
}
Promise.all(promiseArray).then(values=>{
return res.json(values);
},
reason=>{
return res.json(reason);
}).catch(reason=>{
return res.json(reason);
})}
Problem is that promises never return, neither resolved, nor rejected. According to the config file, it has to hit two URLs, say u1 and u2. I tried to log the output to see which requests are returning. When the server is started and very first req is made, U1 returns and req hangs. on refresh I get response from U2,U2 and request hangs, then on refresh again U1,U1 and this continues. It seems to me that for some reason only one request is returned and other sits in buffer or something and come when next request is made. Both requests are being made to the local server only, I am routing it externally just to make use of cache as url is being used as key for cache.
I tried using dummy urls like facebook.com and google.com, and it works perfectly fine.Using one local url and another like facebook.com also works, but when both urls are of local server, it gets stuck.
Does it has any thing to do with single threaded nature of node or due to using same socket for making both requests.
PS- I am using npm-request to make URL calls.
Perhaps hesitating before making the second request would solve your problem.
I've made some tools that could help with that. See the MacroQTools.js file at
https://github.com/J-Adrian-Zimmer/JavascriptPromisesClarified.git
You're defining the request callback as function(success , data), while request consumes error-first callbacks, defined like function(error , response).
You're calling request like:
apiService.get(reqUrl,function(isSuccess,data){
if(!isSuccess){
return reject(data);
}
// console.log(isSuccess);
// console.log(data);
// console.log("coc is ------"+JSON.stringify(op));
resolve({"type": type,"name": op.name,"data": data});
});
Pretending that, if the first parameter misses, you have to reject it with the second parameter, data. While, really, it would something like:
apiService.get(reqUrl,function(err,data){
if(err){
reject(err);
}
else{
// console.log(isSuccess);
// console.log(data);
// console.log("coc is ------"+JSON.stringify(op));
resolve({"type": type,"name": op.name,"data": data});
}
});
Since request expects error-first callbacks (like almost anything in node that takes a callback).
So, when the requests actually work as expected, your code must be actually rejecting the promises with the actual real value, since when the request works, isSuccess is null and data has the real response value.
This surely is breaking something and is not good, while just fixing it maybe doesn't solve your issue completely: I believe your requests are acting weird because some configuration problem of your api, not just because you're rejecting promises when requests are successful (that would just send the data as the rejection reason).
Also you're handling the rejection of Promise.all() twice, passing a second handler to then and calling catch again. Only one is needed, and the .catch(handler) is probably better.
I made a small working example on how you can use Promise.all to collect async requests. I used imdb as the apiService, but any async http service would work too. I didn't reproduce totally from your code, but I'm sure you can adapt this to make your code work, at least the part of the code that is just consuming http services.
var express = require('express');
var app = express();
var Promise = require('bluebird');
var imdb = require('imdb-api');
app.get('/', controllerHandler );
app.listen(3000, function () {
console.log('Example app listening on port 3000!')
});
var apiService = {}
apiService.get = imdb.getReq;
function controllerHandler(request , response){
//like iterating through spotlight.type and returning an array of promises from it.
//in this case the array is from films and Airbag is obviously the best of them
var promises = [{name : 'The Matrix'} , { name : 'Avatar'} , {name : 'Airbag'}].map( createPromise );
//use either .catch(errorHandler) or then( successHandler , errorHandler ). The former is the better:
Promise.all(promises).then( successHandler ).catch( errorHandler );
function successHandler(result){
return response.json(result);
}
function errorHandler(reason){
console.log('There was an error calling to the service:');
console.log(reason);
return response.send('there was an error');
}
}
function createPromise(film){
return new Promise( function(resolve , reject){
apiService.get(film , function(err , data){
if(err)
reject( new Error(err));
else
resolve( {title : data.title , year : data.year} );
});
});
};

Resources