Nodejs Promise "res.end is not a function" error - node.js

I'm refactoring my code to remove a "callback hell" using Promises, but encountered an error that I cannot pass. My code receives list of IDs and processes them making few database calls, that is why I had this "callback hell".
Everything worked fine until Promises. The res is equal 0 when I had to respond back to the client.
function processVMDelete(returnedVMIDs){
return new Promise((resolve, reject) => {
var mariasqlClient = dbConnection();
mariasqlClient.query( sqlUpdateDELETE_STATE_ByVMID, [
'DELETE',
returnedVMIDs
], function(err, rows) {
if (err){
reject(err);
}
console.log('finish update');
// dont' need to return anything here
resolve(0);
});
mariasqlClient.end();
});
}
function getListExpVM(){
return new Promise((resolve, reject) => {
var vmList = [];
var mariasqlClient = dbConnection();
mariasqlClient.query( sqlSearch_ByUSERNAMEAndSTATE, [
requesterUsername,
'ACTIVE'
], function(err, rows) {
if (err){
reject(err);
}
vmList = filterExpiredVMs(rows);
var response = {
status : 200,
success : 'Successfull',
data : vmList,
requester: requesterUsername
};
resolve(response);
});
mariasqlClient.end();
});
}
router.post('/processVMs', function(req, res) {
var returnedVMIDs = JSON.parse(req.body.data);
processVMDelete(returnedVMIDs)
.then(res => {
console.log('done');
// check if there is more available for the user:
getListExpVM()
.then(response => {
console.log('sending back list of VMs');
//===>>> ERROR HERE: res.end is not a function
res.end(JSON.stringify(response));
})
.catch(err => {
console.log('error', err.message);
logger.error("Error getting expired VMs: " + err.message);
//===>>> ERROR HERE: res.send is not a function
res.status(500).send({error: err.message})
});
})
.catch(err => {
console.log('error', err.message);
logger.error("Error processing VMs: " + err.message);
//===>>> ERROR HERE: res.send is not a function
res.status(500).send({error: err.message})
});
});

You've redefined res with this:
processVMDelete(returnedVMIDs)
.then(res => {...})
This will hide the higher scoped res associated with the overall request (the one you need to use for res.end()). Change the name of this one to something else like result and then change the corresponding references that use this result.

Related

Promise not returning resolve from express app.get Nodejs

I'm trying to return a resolve or reject depending on if the promise was successful or not. I can't seem to figure out why this isn't returning the response. All I get from my promise is [object Object]. This is what I get in response.
Here's the code:
app.get('/', (req,res) => {
return new Promise((resolve,reject) => {
var sql = "INSERT INTO usersinfo (firstname,lastname,email,number,latitude,longitude) VALUES(?,?,?,?,?,?)";
conn.query(sql,[fname,lname,email,num,req.query.latitude,req.query.longitude], (err,result) => {
if (err) {
res.send('error')
console.log(err,'there has been an error')
reject('There was an error')
return
}else{
console.log('inserted')
resolve({ success:'true' })
}
res.end()
})
})
})
Where I'm fetching the url:
const res = await fetch(`http://MYIPADDRESS?latitude=${latitude}&longitude=${longitude}`)
console.log(res)
I don't seem to get what's wrong. And if I attach the then((response) => console.log(response)) method I get an error in the expo app saying There was an error sending log messages to your development environment PrettyFormatPluginError:value.hasOwnProperty is not a function. (In 'value.hasOwnProperty('tag')','value.hasOwnProperty is undefined)
You don't need to create a promise. The send method can be called asynchronously -- when the response is ready:
app.get('/', (req,res) => {
var sql = "INSERT INTO usersinfo (firstname,lastname,email,number,latitude,longitude) VALUES(?,?,?,?,?,?)";
conn.query(sql,[fname,lname,email,num,req.query.latitude,req.query.longitude], (err,result) => {
if (err) {
res.send('error');
console.log(err,'there has been an error');
} else {
res.send({ success:'true' }); // <---
console.log('inserted');
}
});
});
NB: also, there is no need to call res.end() as res.send() already implies that.
On the client side you'll have to await the JSON content to be generated:
const response = await fetch(`http://MYIPADDRESS?latitude=${latitude}&longitude=${longitude}`);
const result = await response.json(); // <---
console.log(result);

Render the results of two separate async.each methods

I am new to nodejs and async. Having trouble understanding how can I wrap the two separate async.each methods to have one res.render...I am trying to display a list of valid account ids, and valid user ids on the front end.
The two separate async.each methods are:
async.each(account_ids, function(accountId, callback) {
console.log('Processing accountId ' + accountId);
callingExternalApi(accountId, callback, function(err, response){
if(err){
console.log("error account");
}
console.log("account response is: ", response);
});
}, function(err) {
if( err ) {
console.log('An account failed to process');
} else {
console.log('All accounts have been processed successfully');
}
});
and
async.each(email_ids, function(emailId, callback) {
console.log('Processing email id ' + emailId);
request({
url: emailIdlookupUrl,
method: 'POST',
json: {
email_address: emailId
}
}, function (err, response, body) {
if (err) {
logger.error(err);
req.flash('error', err.message);
return res.redirect('?');
}
if (response.statusCode !== 200) {
const msg = 'Unable to verify user';
req.flash('error', msg);
return res.redirect('?');
}
console.log("user id is: ", body.user.id);
callback();
});
}, function(err) {
if( err ) {
console.log('An email failed to process');
} else {
console.log('All user emails have been processed successfully');
}
});
Any help is highly appreciated. Please excuse me for any redundant callbacks or error logging. Still learning nodejs.
Thanks!!
The main issue is not that you are invoking both of these async.each calls. The problem is that they will run in parallel, and the fastest one to invoke req.* functions or callback function will return a response to the connection.
Both of these functions return promises if their callback parameters are omitted.
I recommend reading up on both the async library and JS async/await in general:
https://javascript.info/async-await
https://caolan.github.io/async/v3/docs.html#each
https://zellwk.com/blog/async-await-express/
Note that async also accepts native async functions, which many finder cleaner and easier to understand.
Here is what I think you want from the code above, including compiling the results into lists:
var request = require("request-promise");
async function checkAccounts(account_ids) {
const valid_accounts = [];
await async.each(account_ids, async function(accountId) {
console.log("Processing accountId " + accountId);
const extAPIresult = await callingExternalApi(accountId);
console.log("account response is: ", extAPIresult);
});
valid_accounts.push(extAPIresult);
console.log("All accounts have been processed successfully");
return valid_accounts;
}
async function checkEmails(email_ids) {
const valid_emails = [];
await async.each(email_ids, async function(emailId) {
console.log("Processing email id " + emailId);
const reqresult = await request({
url: emailIdlookupUrl,
method: "POST",
json: {
email_address: emailId
}
});
if (reqresult.statusCode !== 200) {
throw new Error("Unable to verify user");
}
valid_emails.push(reqresult.body.user.id);
console.log("user id is: ", reqresult.body.user.id);
});
console.log("All emails have been processed successfully");
return valid_emails;
}
async function doChecks() {
const accounts = checkAccounts(account_ids);
const emails = checkEmails(email_ids);
const responses = await Promises.all([accounts, emails]);
console.log("All checks have been processed successfully");
return responses;
}
function get(req, res) {
doChecks()
.then(responses => {
res.send("All checks have been processed successfully");
res.send(String(responses));
})
.catch(err => {
req.flash("error", err.message);
res.redirect("?");
});
}

How to catch an empty reply from an API {}

I'm currently using Node.js to fetch an API data and converting it into my own Express.js API server to send my own data (The 2 APIs I'm using changes the structure sometime and I have some users that need to keep the same structure).
So here is the code I'm using
app.get('/app/account/:accountid', function (req, res) {
return fetch('https://server1.com/api/account/' + req.params.accountid)
.then(function (res) {
var contentType = res.headers.get("content-type");
if (contentType && contentType.includes("application/json")) {
apiServer = 'server1';
return res.json();
} else {
apiServer = 'server2';
throw "server1 did not reply properly";
}
}).then(server1Reconstruct).then(function (json) {
res.setHeader('Content-Type', 'application/json');
return res.send(json);
}).catch(function (err) {
console.log(err);
}).then(function () {
if (apiServer == 'server2') {
server2.fetchAccount({
accountID: [Number(req.params.accountid)],
language: "eng_us"
})
.then(server2Reconstruct)
.then(function (json) {
res.setHeader('Content-Type', 'application/json');
return res.send(json);
}).catch(function (err) {
console.log(err);
});
}
});
})
To quickly explain the code: I call server1 through a normal Fetch this answer might be {} which is where I have a problem. If the accountid doesn't exist the server returns an JSON response with no errors to grab...
What should I do to be able to catch it... And If I catch it switch to server 2.
(Don't be too confused about server2 call as it's another package).
If I understand your problem correctly, you should follow those steps :
fetch the initial API
call the .json() method on the result - which returns a promise
deal with the json response in the first .then(json => ...), and here check if the result is {} then call server2, else call server1
BTW, your code looks very messy with all those then and catch, I recommend putting some stuff into functions, and using async/await if you can.
Here is some pseudo-code sample that you could use :
function server2Call() {
return server2.fetchAccount({
accountID: [Number(req.params.accountid)],
language: 'eng_us'
})
.then(server2Reconstruct)
.then(function (json) {
res.setHeader('Content-Type', 'application/json');
return res.send(json);
}).catch(function (err) {
console.log(err);
});
}
app.get('/app/account/:accountid', function (req, res) {
return fetch('https://server1.com/api/account/' + req.params.accountid)
.then(res => {
var contentType = res.headers.get('content-type');
if (contentType && contentType.includes('application/json')) {
return res.json();
} else {
server2Call()
}
})
.then(json => {
res.setHeader('Content-Type', 'application/json');
if (json is {}) return server2Call()
else return res.send(json);
}).catch(function (err) {
console.log(err);
})
});

Saving replaced string data in mongodb using nodejs

I am trying to replace a string in url . Here is image of it
in this image I want to replace lssplalpha with lssplprod which are in pics array. For that I created an api . Here is a code
apiRoutes.get('/SchoolListing_lssplalpha_Replace',function(req, res) { schoolListModel.find({},function(err,check){
if(err){
return console.log(err);
}
else{
for(var i=0;i<check.length;){
var f=0;
for(var j=0;j<check[i].pics.length;j++){
f++;
var newTitle = check[i].pics[j].replace("lssplalpha","lsslprod");
check[i].pics[j] = newTitle;
console.log("after change",check[i].pics[j]);
check[i].save(function (err) {
if(err) {
console.error('ERROR!');
}
});
}
if(j==check[i].pics.length&&j==f){
i++;
}
}
console.log("i value",i);
console.log("check length",check.length);
if(i==check.length){
return res.json({status:true,message:"Updated Schools"}); }
}
});
});
I am getting success response . When I go and check database nothing changed in db. To know the reason I write log of it. When I see logs it was replacing correctly. But I didn't understand why those are not reflecting in database? Here is an image of log in console
Please help me to come out of this
The issue here is you are running a for loop (synchronous) where you are calling the model.save() operation which is asynchronous and the loop keeps iterating but the results of the async calls come later. The process of saving a database item in an array takes some time and Node.js knows this, so it starts the update and then just moves on trying to update the next item in the array. Once the write operation is complete a callback function is run, but by that point the loop has completed and there is no way to know which items finish in what order.
You could use the Bulk Write API to update your models. This allows you to sends multiple write operations to the MongoDB server in one command. This is faster than sending multiple independent operations (like) if you use create()) because with bulkWrite() there is only one round trip to MongoDB.
The following examples show how you can use the bulkWrite.
Using async/await:
apiRoutes.get('/SchoolListing_lssplalpha_Replace', async (req, res) => {
try {
let ops = [];
const docs = await schoolListModel.find({}).lean().exec();
docs.forEach(doc => {
const pics = doc.pics.map(pic => pic.replace("lssplalpha", "lsslprod"));
ops.push({
"updateOne": {
"filter": { "_id": doc._id },
"update": {
"$set": { pics }
}
}
});
});
const result = await schoolListModel.bulkWrite(ops);
console.log('Bulk update complete.', result);
res.status(200).json({
status: true,
message: "Updated Schools"
});
} catch (err) {
res.status(400).send({
status: false,
message: err
});
}
});
Using Promise API:
const bulkUpdate = (Model, query) => (
new Promise((resolve, reject) => {
let ops = [];
Model.find(query).lean().exec((err, docs) => {
if (err) return reject(err);
docs.forEach(doc => {
const pics = doc.pics.map(pic => (
pic.replace("lssplalpha", "lsslprod")
));
ops.push({
"updateOne": {
"filter": { "_id": doc._id },
"update": {
"$set": { pics }
}
}
});
if (ops.length === 500) {
Model.bulkWrite(ops).then((err, result) => {
if (err) return reject(err);
ops = [];
resolve(result);
});
}
});
if (ops.length > 0) {
Model.bulkWrite(ops).then((err, result) => {
if (err) return reject(err);
resolve(result);
});
}
});
})
);
apiRoutes.get('/SchoolListing_lssplalpha_Replace', (req, res) => {
bulkUpdate(schoolListModel, {}).then(result => {
console.log('Bulk update complete.', result);
res.status(200).json({
status: true,
message: "Updated Schools"
});
}).catch(err => {
res.status(400).send({
status: false,
message: err
});
});
});
You are running asynchronous call model.save() in for loop(synchronous)
To make your for loop synchronous you can use for...of loop which works asynchronous, also you will not need to add multiple checks like you have done in your code.
Try following code, it will work
apiRoutes.get('/SchoolListing_lssplalpha_Replace', function (req, res) {
schoolListModel.find({},function (err, check) {
if (err) {
return console.log(err);
}
else {
for (let checkObj of check) {
let newPicArr=[];
for (let pic of checkObj.pics) {
pic = pic.replace("lssplalpha", "lsslprod");
newPicArr.push(pic);
}
checkObj.pics=newPicArr;
checkObj.save(function (err) {
if (err) {
console.error('ERROR!');
}
});
}
return res.json({ status: true, message: "Updated Schools" });
}
});
});

Fetching Multiple Https request in Nodejs using ejs templates

Getting the first [ const p1] HTTPS request, but unable to fetch the second one [const p2] its showing me undefined.Where im missing
function fetchJSON(url) {
return new Promise((resolve, reject) => {
request(url, function(err, res, body) {
if (err) {
reject(err);
} else if (res.statusCode !== 200) {
reject(new Error('Failed with status code ' + res.statusCode));
} else {
resolve(JSON.parse(body));
}
});
});
}
router.get('/news-and-media',function(req,res,next){
const p1 = fetchJSON('http://example.com/wsplus/abs/123');
const p2 = fetchJSON('http://example.com/blsd/blog_posts/312');
Promise.all([p1],[p2]).then((data) => {
console.log(data[0]); // getting data
console.log(data[1]); // this giving me undefined
res.render("news-and-media", { getdata: data[0],banner:data[1]} );
}).catch(err => console.error('There was a problem', err));
});
Don't use
Promise.all([p1], [p2])
but
Promise.all([p1, p2])
According to the Promise.all() documentation which is saying :
Promise.all(iterable);

Resources