currently I have a simple express route with nodejs. If a certain condition is met in a mongoDB query i want to redirect the user and stop any further code from executing.
Some things I have already tried:
- return
- res.end()
An example of code is below
router.post("/test", (req, res) => {
Col.findOne({ 'val': req.body.val })
.then(val => {
if (val) {
// 1st attempt
return res.redirect("/testing");
//2nd attempt
res.redirect("/testing");
res.end();
// 3rd attempt
res.redirect("/testing");
return;
}
})
console.log("Do NOT want this to execute but it does anyways");
}));
The issue is that after the code in the (if) statement executes and returns / res.end(), the code outside the .then() still executes
On mongoDB .findOne would return null if it didn't find any matching document as per given filter query. In case of falsy values then you need to have else block in order to get out of execution(Here you actually got a proper response from DB not an error - but it might be a falsy value null). Please add .catch() block as well for any errors.
router.post("/test", (req, res) => {
Col.findOne({ 'val': req.body.val })
.then(val => {
if (val) {
return res.redirect("/testing");
} else {
return res.send('No val found')
}
}).catch((err) => {
console.log('In Err ::', err)
return res.send('Some error occurred')
})
})
Using Async/await in Express
router.post("/test", async (req, res) => {
const val = await Col.findOne({val: req.body.val});
if (val) {
res.redirect("/testing");
} else {
// other code goes here
}
}));
Related
I have these two endpoints (it's simplified but the idea/problem remains the same) ...
/api/v1/update
exports.update = (req, res) => {
Data.update({salary: 1})
.then(data => {
console.log("All good")
return res.status(200).send({message: "OK"})
})
.catch(error => {
console.log("Oh Crap!")
return res.status(400).send(error)
})
}
and
/api/v1/process in which I call the first one as part of the processing
exports.process = (req, res) => {
axios.get('http://localhost:8008/api/v1/update')
.then(data => {
console.log("You got it")
return res.status(200).send(data)
})
.catch(error => {
console.log("Not working!")
return res.status(400).send(error)
})
}
This is what I getting in the console:
$>: nodemon server.js
Server running on port 8008
All good
Not working!
How come the axios call is successfully (the "update" endpoint) finished but it falls into CATCH instead of THEN ?
What am I missing?
EDIT:
ok I figured it out..the error was in the return res.status(200).send(data) of /api/v1/process ...
Error: Converting circular structure to JSON
So what I did was I extracted the values (response of the first endpoint -> update) and send it afterwards :) it's working great now ... silly mistake
facepalm
the error was in the return res.status(200).send(data) of /api/v1/process ...
Error: Converting circular structure to JSON
So what I did was I extracted the values (response of the first endpoint -> update) and send it afterwards :) it's working great now ... silly mistake
i think the problem is that the controller return undefined, only after executing then it tries to return and execute the res.send.
For this kinda problems i use async/await to simplify async calls
exports.update = async (req, res) => {
try {
await Data.update({salary: 1});
res.status(200).send({message: "OK"});
} catch(error) {
console.log("Oh Crap!")
res.status(400).send(error)
}
}
Also check the error details in axios:
.catch(error => {
console.log("Not working!", error);
return res.status(400).send(error);
})
let data = users.find({})
.toArray()
.then((result) => {
state.response.end(JSON.stringify({
message: result
}));
})
.catch((err) => {
state.response.end(JSON.stringify({
error: err.message,
}));
log.error(failed to process the request: ", err);
});
if(!!data){
return collage.find({_id: params._id}).toArray()
.then((collages: any[]) => {
if (!!collages) {
return //some more code
}
})
.then(() => {
return university.find({_id: params._id}).toArray()
})
.then((user: any[]) => {
if (!!user) {
return //some more code
}
})
.then(() => {
response.writeHead(200, {'Content-Type': 'application/json'});
response.end(JSON.stringify({status: " Request processed."}));
})
.catch((err) => {
response.writeHead(200, {'Content-Type': 'application/json'});
response.end(JSON.stringify({status: "error", + err.message}));
});
}
return all promises result at once like
return {
collage:collages,
university:users
}
Here i am fetching data from multiple collections using promise(no async and await). i want to retrieve the data from users collections first then from other collections. code inside if condition returning first without waiting users collections results. is it write way to do that ? or?
I think you are new to Nodejs never mind, Before beginning to code, you should understand synchronous code vs asynchronous code. Your code is not getting executed synchronously. Read this out:
https://codeforgeek.com/asynchronous-programming-in-node-js/
https://javascript.info/async
Also, learn callbacks, promises, and async-await
Note: I am not checking logic here I am just guiding you with the flow, Please check all condition by yourself
You can use async-await
try {
let data = await users.find({}).toArray()
if(check proper condition here){
let collages = await collage.find({_id: params._id}).toArray()
so and so.....
}
catch(err) {
}
==============================Using Promises==========================
let final_result = {};
users.find({}).toArray()
.then((result) => {
let data = result;
state.response.end(JSON.stringify({
message: result
}));
if(check for condition)
return collage.find({_id: params._id}).toArray();
}).then(function(collages){
final_result.collages = collages;
return university.find({_id: params._id}).toArray()
}).then((users) => {
final_result.users= users
so and so ......
}).catch((err) => {
state.response.end(JSON.stringify({
error: err.message,
}));
log.error(failed to process the request: ", err);
});
data is not defined when you access it a second time. That's how asynchronous code works, there's a guide on it if you're not familiar with the concept, but basically .toArray() is a promise that resolves to a value, it is not a value.
Here's what you could do:
// using .then
users.find({}).toArray().then(data => {
// data is defined, do something with it
});
// using async/await
const data = await users.find({}).toArray();
I have a generic Node+Express server where I serve GET requests. Some of these GET requests need multiple DB queries which are callbacks.
Here is an example of my code:
GET router:
router.get('/getbalance', function(req, res, next) {
wallet.createNewAddress()
.then(result => {
res.send(result);
})
.catch(err => {
console.log(err);
});
This is the function with callbacks:
async createNewAddress()
{
pool.query(`SELECT ...`)
.then (dbres1 => {
pool.query(`SELECT ...`)
.then(dbres2 => {
(async() => {
var pubkeys = await this.getPublicKeysFromIndexes(wallet.id, index_wallet_1, index_wallet_2, index_wallet_3);
var script = this.generateScript(pubkey1, pubkey2, pubkey3);
})();
})
.catch(e => {
console.log(e.stack);
})
}
})
.catch(e => {
console.log(e.stack);
});
}
I have removed long statements for brevity.
As you can see, I have multiple levels of nested promises.
What is the proper way to handle a request like this? Should I return each promise or should I run everything synchronously using async()?
What I need to do is to return the script at the very middle of the statements. This last call that returns the script is a normal synchronous function.
Appreciate any advice.
Thank you.
I believe using async/await will give you much more readable code, while essentially following the same logic. Of course you will have to be aware that you'll need to add try/catch handler(s) to the code.
If you use async/await you'll end up with something like this:
async function createNewAddress()
{
try {
let dbres1 = await pool.query(`SELECT ...`);
let dbres2 = await pool.query(`SELECT ...`);
var pubkeys = await this.getPublicKeysFromIndexes(wallet.id, index_wallet_1, index_wallet_2, index_wallet_3);
return this.generateScript(pubkey1, pubkey2, pubkey3);;
} catch (err) {
// ok something bad happened.. we could skip this handler and let the error bubble up to the top level handler if we're happy with that approach.
console.error(err);
// Rethrow or create new error here.. we don't want to swallow this.
throw err;
}
}
You can then call as before:
router.get('/getbalance', function(req, res, next) {
wallet.createNewAddress()
.then(result => {
res.send(result);
})
.catch(err => {
console.log(err);
});
Or use an async handler:
router.get('/getbalance', async function(req, res, next) {
try {
let result = await wallet.createNewAddress();
res.send(result);
} catch (err) {
// Also consider sending something back to the client, e.g. 500 error
console.log(err);
};
})
I want my website to respond after a successful api call, which is initiated via a post request and also changes some database values.
If there was NO post request the site should also load as usual.
If I do something like this, then the site is getting loaded as usual and then I get an error because of the second rendering attempt.
I guess because node does wait for the receipt, but in parallel does already execute the loadNewSite() function:
app.all('/customer', function(req, res) {
if (Object.keys(req.body).length != 0) {
apiCall(someParameter)
.on('error', error => {console.log(error);} )
.on('receipt', function() {loadNewSite();} );
}
function loadNewSite() {
return res.render('site.html');
}
loadNewSite()
})
try removing the last loadNewSite() as one is already called when you on reciept
check with req.method whether it's a POST request or not.
app.all('/customer', function(req, res) {
// if method is not post handle seperately
if(req.method != 'POST'){
return loadNewSite('site.html');
}
if (Object.keys(req.body).length != 0) {
apiCall(someParameter)
.on('error', error => {console.log(error);} )
.on('receipt', function() {loadNewSite();} );
}
function loadNewSite() {
return res.render('site.html');
}
})
I would create a promise to execute the Api call and resolve it on receipt or reject it on error. Then make the callback async and await for the api call promise.
I left the final call to loadNewSite in case of error, obviously you can modify it and make a function that maybe returns something different in error case.
const execApiCall = (params) => {
return new Promise((resolve, reject) => {
apiCall(params)
.on('error', error => {reject(error);} )
.on('receipt', function() {resolve();} );
})
};
app.all('/customer', async function(req, res) {
function loadNewSite() {
return res.render('site.html');
}
if (Object.keys(req.body).length != 0) {
try {
await execApiCall(params);
return loadNewSite();
} catch (e) { //handle errors }
}
loadNewSite()
})
I have the following Express endpoint:
const all = require('promise-all');
router.post('/verify', upload.single('photo'), async (req, res) => {
...
await all({'p1': p1, 'p2': p2}).then((response) => {
...
console.log("Response:",
ruleCtrl.manageRule(detection, res);
});
});
ruleCtrl.manageRuleis as follows:
export async function manageRule(identifierDetected, res) {
let rule = db.getRule(identifierDetected);
await all([rule]).then((ruleExtracted) => {
...
res.json(ruleExtracted);
}).catch((err) => {
res.status(418).send("DOCUMENT_NOT_RECOGNIZED");
});
}
and db.getRule:
export async function getRule(idRule) {
return new Promise((resolve, reject) => {
Rule.findOne({ruleID: idRule}, (err, rule) => {
if (err) {
reject("MongoDB Rule error: " + err);
} else {
resolve(rule);
}
});
})
}
My response is into manageRule and this function depends of the values extracted into the await all. So, right now, Express is returning a response before get the information from mongoose database (db).
Which is the way to handle this issue?
Thanks everyone!
I would refactor your code a bit to make it easier to read, and also return the result from ruleCtrl.manageRule(detection, res);.
The request might simply be timing out since your original code is missing a return there or an await (to make sure it finishes executing)
Express endpoint:
const all = require('promise-all');
router.post('/verify', upload.single('photo'), async (req, res) => {
...
// Catch any exceptions from the promises. This is the same as using .catch
try {
// Lets assign the returned responses to variable
let [p1Result, p2Result] = await all({'p1': p1, 'p2': p2});
...
console.log("Responses:", p1Result, p2Result);
// return the response from manageRule method
return ruleCtrl.manageRule(detection, res);
} catch(err) {
// Handle err here
}
});
One of the great benefits with async await is moving away from chained promises, so simply return the result from the await to a variable instead of using .then()
ruleCtrl.manageRule
export async function manageRule(identifierDetected, res) {
// Use try catch here to catch error from db.getRule. Assign to variable and return
// res.json
try {
let ruleExtracted = await db.getRule(identifierDetected);
...
return res.json(ruleExtracted);
} catch(err) {
return res.status(418).send("DOCUMENT_NOT_RECOGNIZED");
}
}
You dont have to return res.json or res.status here, I just like to keep track of when I want to end function execution.
You could refactor the ruleCtrl.manageRule method even further by not sending in res as a parameter but by returning the result from db.getRule instead. Let router.post('/verify) handle req and res, so to make it even easier to read.