Express router callbacks - javascript

I'm a little new to Express/Node.js/Mongoose and I've ran into callback hell. What I'm trying to do is get a request in to this API URL /page/module/add/:id, if successful call buildMod(data), then that function calls getMod(data), and then that function calls writeMod(data) and eventually I want to pass the true value right back up to my router.
Once I have the response, I want to return it. I've searched online and there's not many similar situations--I personally think I've got myself in too deep...
router.get('/page/module/add/:id', function(req, res) {
Client.find({"emailAddress": emailAddress, "sequence.slug": pageSlug},
{"emailAddress": 1, "sequence.$": 1}, function (err, data) {
if (!err) {
res.statusCode = 200;
buildMod(data);
return res.json(data);
} else {
res.statusCode = 500;
log.error('Internal error(%d): %s', res.statusCode, err.message);
return res.json({
error: 'Server error'
});
}
}).select('sequence emailAddress domain');
});
function buildMod(data) {
getMod(data);
}
function getMod(data) {
Module.find({ 'module_id': moduleNumID }, function (err, module) {
if(!module) {
return false;
}
if (!err) {
writeMod(data);
} else {
return false;
}
});
}
function writeMod(data) {
fs.appendFile(location, content, function(err) {
if (err) throw err;
return true;
});
}
I know the declarations are wrong for the functions for callbacks but I've been trying and I just can't seem to get past this stage. I'm sure this is definitely possible, any help is really appreciated!

fs.appendFile is asynchronous and you can not return from asynchronous calls.
Make use of callback
router.get('/page/module/add/:id', function(req, res) {
Client.find({
"emailAddress": emailAddress,
"sequence.slug": pageSlug
}, {
"emailAddress": 1,
"sequence.$": 1
}, function(err, data) {
if (!err) {
res.statusCode = 200;
buildMod(data, function(data) {
res.json(data);
});
} else {
res.statusCode = 500;
log.error('Internal error(%d): %s', res.statusCode, err.message);
return res.json({
error: 'Server error'
});
}
}).select('sequence emailAddress domain');
});
function buildMod(data, cb) {
getMod(data, cb);
}
function getMod(data, cb) {
writeMod(data, cb);
}
function writeMod(data, cb) {
fs.appendFile(location, content, function(err) {
if (err) throw err;
cb(true);
});
}

Related

Why is my response always http 400 status code

I'm learning Node.js and I'm working on a sample app. I've a question why is that I always receive http 400 even if it is a successful response.
abcRoutes.get('/fetch', function (req, res) {
abc.getInfo(req,(err,response) =>{
if(err){
res.status(400).send(err);
}else{
res.status(200).send(response);
}
})
});
var getInfo = (req, callBack) => {
***some processing***
if (err) {
callBack(err);
} else {
callBack(result);
}
});
client.close();
}
})
}
The error is here:
var getInfo = (req, callBack) => {
//* **some processing***
if (err) {
callBack(err)
} else {
callBack(null, result) <---
}
}
Using callback pattern you need to keep in mind that the first argument is always an error.
You should set the first argument (err) to null on success:
if (err) {
callBack(err);
}
else {
callBack(null, result);
}

How to perform looped queries in transactions properly with node.js, mssql and msnodesqlv8

I am attempting to do what I've previously done in c# and asp.net webforms and execute a number of insert stored procedures, some of which are in loops, in a transaction. I am now trying to do so in my angular application through node.js with the mssql package. What I have so far is as follows.
//Insert Change Record
router.post('/insertChange', (req, res) => {
const transaction = new sql.Transaction(conn);
transaction.begin(err => {
let rolledBack = false;
transaction.on('rollback', aborted => {
rolledBack = true;
})
const request = new sql.Request(transaction);
request.input('ChangeTitle', req.body.changeTitle);
request.input('TypeId', req.body.typeId);
request.input('DateSubmitted', req.body.dateSubmitted);
request.input('TargetDate', req.body.targetProductionDate);
request.input('ChangeSponsor', req.body.changeSponsor);
request.input('UATDate', req.body.dateReadyUAT);
request.input('ChangeSponsorEmail', req.body.changeSponsorEmail);
request.input('ClarityId', req.body.clarityId);
request.input('ChangeDescription', req.body.changeDescription);
request.input('ComponentName', req.body.componentName);
request.input('ComponentDescription', req.body.componentDescription);
request.input('ReasonForChange', req.body.reasonForChange);
request.input('ComponentREplacing', req.body.componentReplacing);
request.input('DependentChange', req.body.dependentChange);
request.input('InstallOption', req.body.installOption);
request.input('RebootRequired', req.body.rebootRequired);
request.input('UserIntervention', req.body.userIntervention);
request.input('Activation', req.body.activation);
request.input('ContingencyPlan', req.body.contingencyPlan);
request.input('AdditionalInformation', req.body.additionalInformation);
request.input('PerformanceImpact', req.body.applicationPerformance);
request.input('IsPCoERequired', req.body.pcoeTesting);
request.input('IsCanadianRetailBranch', req.body.pbsTesting);
request.input('BusinessAppImpact', req.body.businessApplication);
request.input('NetworkImpact', req.body.networkPerformance);
request.input('NewInfrastructure', req.body.newInfrastructure);
request.input('LogonTime', req.body.logonTime);
request.input('AdditionalTechnicalInformation', req.body.additionalAssessmentInfo);
request.input('IsProdIssue', req.body.isProdIssue);
request.input('ProdIssue', req.body.productionIssue);
request.input('ProdIncidentNum', req.body.incidentNumbers);
request.input('IsMajorChange', req.body.isMajorChange);
request.input('HasRequiredTesting', req.body.hasRequiredTesting);
request.input('HasPackageSubmit', req.body.hasPackageSubmit);
request.input('SignOffETA', req.body.signoffETA);
request.input('SpecificTesting', req.body.specificTesting);
request.input('SpecificPilot', req.body.specificPilot);
request.input('PilotInfo', req.body.pilotTransits);
request.input('UserChanges', req.body.userDifferences);
request.input('ServiceDeskProcedure', req.body.procedureSupport);
request.input('SupportCallFlowId', req.body.supportCallFlow);
request.input('OverallChangeStatus', 1);
let changeId = request.output('changeId', sql.Int);
request.execute('dbo.InsertChange').then(function (result) {
console.dir(result);
}).catch(function (err) {
console.dir(err);
});
async.each(
req.body.changeType
,function iterator(item, next) {
const requestCT = new sql.Request(transaction);
requestCT.input('ChangeId', changeId);
requestCT.input('TypeOfChangeId', item.typeOfChangeId);
requestCT.input('Description', item.description);
requestCT.execute('dbo.InsertTypeOfChange').then(function (result) {
console.dir(result);
next();
}).catch(function (err) {
console.dir(err);
});
})
async.each(
req.body.serviceImpacted
,function iterator(item, next) {
const requestSI = new sql.Request(transaction);
requestSI.input('ChangeId', changeId);
requestSI.input('ServicesImpactedId', item.serviceImpactedId);
requestSI.execute('dbo.InsertImpactedService').then(function (result) {
console.dir(result);
next();
}).catch(function (err) {
console.dir(err);
});
})
async.each(
req.body.businessImpacted
,function iterator(item, next) {
const requestBI = new sql.Request(transaction);
requestBI.input('ChangeId', changeId);
requestBI.input('BusinessImpactedId', item.businessImpactedId);
requestBI.execute('dbo.InsertImpactedBusiness').then(function (result) {
console.dir(result);
next();
}).catch(function (err) {
console.dir(err);
});
})
async.each(
req.body.criticalApp
,function iterator(item, next) {
const requestCA = new sql.Request(transaction);
requestCA.input('ChangeId', changeId);
requestCA.input('CriticalBankingId', item.criticalId);
requestCA.input('Description', item.description);
requestCA.execute('dbo.InsertCriticalBankingApp').then(function (result) {
console.dir(result);
next();
}).catch(function (err) {
console.dir(err);
});
})
async.each(
req.body.testStages
,function iterator(item, next) {
const requestTS = new sql.Request(transaction);
requestTS.input('ChangeId', changeId);
requestTS.input('TestStageId', item.testStageId);
requestTS.input('TestDate', item.testDate);
requestTS.input('Description', item.description);
requestTS.execute('dbo.InsertChangeTest').then(function (result) {
console.dir(result);
next();
}).catch(function (err) {
console.dir(err);
});
})
if (err) {
if (!rolledBack) {
transaction.rollback(err => {
console.dir(err);
// ... error checks
})
}
} else {
transaction.commit(err => {
console.dir(err);
// ... error checks
})
}
})
});
There really isn't a heck of a lot of material online anywhere for processing transactions with stored procedures, and even less for big transactions like this. But the error I'm getting is the following multiple times in my terminal log.
{ TransactionError: Can't acquire connection for the request. There is another request in progress.
at Transaction.acquire (C:\Users\Redirection\meecd26\Documents\GitRepo\td-angular-starter\node_modules\mssql\lib\base.js:740:30)
at Immediate._query.err [as _onImmediate] (C:\Users\Redirection\meecd26\Documents\GitRepo\td-angular-starter\node_modules\mssql\lib\msnodesqlv8.js:417:19)
at runCallback (timers.js:781:20)
at tryOnImmediate (timers.js:743:5)
at processImmediate [as _immediateCallback] (timers.js:714:5) code: 'EREQINPROG', name: 'TransactionError' }
This certainly suggests that I'm performing the transaction improperly.
Would someone be able to help guide me as to how I can improve and fix this?
Edit: Made changes to my code to reflect what I found here, as well as a few changes to my first insert. While my first insert does work, I'm still getting the repeated errors above, so now I know my problems are with how I'm implementing the inserts inside of loops. Is there a way I can close the connection upon each insert? Would that be the approach I should try and take?
I ended up getting it working, and although my understanding of how everything works admittedly hasn't improved much at all, I can at least post what I have in hopes of others hopefully finding it helpful.
router.post('/insertChange', (req, res) => {
console.log("Beginning of POST, before initializing transaction");
let changeId;
let mainInsert = false;
beginTransaction(function (err, rollback, transaction) {
if (err) {
// return cb(err);
}
let request = new sql.Request(transaction);
// request.verbose = true;
request.input('ChangeTitle', req.body.ChangeTitle);
request.input('TypeId', req.body.TypeId);
request.input('DateSubmitted', req.body.DateSubmitted);
request.input('TargetDate', req.body.TargetDate);
request.input('ChangeSponsor', req.body.ChangeSponsor);
request.input('UATDate', req.body.UATDate);
request.input('ChangeSponsorEmail', req.body.ChangeSponsorEmail);
request.input('ClarityId', req.body.ClarityId);
request.input('ChangeDescription', req.body.ChangeDescription);
request.input('ComponentName', req.body.ComponentName);
request.input('ComponentDescription', req.body.ComponentDescription);
request.input('ReasonForChange', req.body.ReasonForChange);
request.input('ComponentReplacing', req.body.ComponentReplacing);
request.input('DependentChange', req.body.DependentChange);
request.input('InstallOption', req.body.InstallOption);
request.input('RebootRequired', req.body.RebootRequired);
request.input('UserIntervention', req.body.UserIntervention);
request.input('Activation', req.body.Activation);
request.input('ContingencyPlan', req.body.ContingencyPlan);
request.input('AdditionalInformation', req.body.AdditionalInformation);
request.input('PerformanceImpact', req.body.PerformanceImpact);
request.input('IsPCoERequired', req.body.IsPCoERequired);
request.input('IsCanadianRetailBranch', req.body.IsCanadianRetailBranch);
request.input('BusinessAppImpact', req.body.BusinessAppImpact);
request.input('NetworkImpact', req.body.NetworkImpact);
request.input('NewInfrastructure', req.body.NewInfrastructure);
request.input('LogonTime', req.body.LogonTime);
request.input('AdditionalTechnicalInformation', req.body.AdditionalTechnicalInformation);
request.input('IsProdIssue', req.body.IsProdIssue);
request.input('ProdIssue', req.body.ProdIssue);
request.input('ProdIncidentNum', req.body.ProdIncidentNum);
request.input('IsMajorChange', req.body.IsMajorChange);
request.input('HasRequiredTesting', req.body.HasRequiredTesting);
request.input('HasPackageSubmit', req.body.HasPackageSubmit);
request.input('SignOffETA', req.body.SignOffETA);
request.input('SpecificTesting', req.body.SpecificTesting);
request.input('SpecificPilot', req.body.SpecificPilot);
request.input('PilotInfo', req.body.PilotInfo);
request.input('UserChanges', req.body.UserChanges);
request.input('ServiceDeskProcedure', req.body.ServiceDeskProcedure);
request.input('SupportCallFlowId', req.body.SupportCallFlowId);
request.input('OverallChangeStatus', 1);
request.output('changeId', sql.Int);
request.execute('dbo.InsertChange', function (err, callback) {
if (err) {
console.dir(err);
rollback(err);
res.status(400).send("Failed to submit change form.");
} else {
mainInsert = true;
}
if (callback) {
changeId = +callback.returnValue;
}
if (mainInsert) {
async.each(
req.body.changeType, function iterator(item, next) {
request = new sql.Request(transaction);
request.input('ChangeId', changeId);
request.input('TypeOfChangeId', item.typeOfChangeId);
request.input('Description', item.description);
request.execute('dbo.InsertTypeOfChange', function (err, callback) {
if (err) return next(err);
console.dir(callback);
next();
})
}, function fin(err) {
if (err) {
rollback(err);
return; //done
}
})
async.each(
req.body.serviceImpacted, function iterator(item, next) {
request = new sql.Request(transaction);
request.input('ChangeId', changeId);
request.input('ServicesImpactedId', item.serviceImpactedId);
request.execute('dbo.InsertImpactedService', function (err, callback) {
if (err) return next(err);
console.dir(callback);
next();
})
}, function fin(err) {
if (err) {
rollback(err);
return; //done
}
})
async.each(
req.body.businessImpacted, function iterator(item, next) {
request = new sql.Request(transaction);
request.input('ChangeId', changeId);
request.input('BusinessImpactedId', item.businessImpactedId);
request.execute('dbo.InsertImpactedBusiness', function (err, callback) {
if (err) return next(err);
console.dir(callback);
next();
})
}, function fin(err) {
if (err) {
rollback(err);
return; //done
}
})
async.each(
req.body.criticalApp, function iterator(item, next) {
request = new sql.Request(transaction);
request.input('ChangeId', changeId);
request.input('CriticalBankingId', item.criticalId);
request.input('Description', item.description);
request.execute('dbo.InsertCriticalBankingApp', function (err, callback) {
if (err) return next(err);
console.dir(callback);
next();
})
}, function fin(err) {
if (err) {
rollback(err);
return; //done
}
})
async.each(
req.body.testStages, function iterator(item, next) {
request = new sql.Request(transaction);
request.input('ChangeId', changeId);
request.input('TestStageId', item.testStageId);
request.input('TestDate', item.testDate);
request.input('Description', item.description);
request.execute('dbo.InsertChangeTest', function (err, callback) {
if (err) return next(err);
console.dir(callback);
next();
})
}, function fin(err) {
if (err) {
rollback(err);
return; //done
}
else {
transaction.commit(function (err) {
if (err) {
console.error('Transaction commit error: ', err);
res.status(400).send("Failed to submit change form.");
} else {
console.error('Transaction commit success');
res.send({ "Response": "Success", "Message": "Change form successfully submitted." });
}
});
}
})
}
});
})
});

nodejs express response append into a list

I have multiple res.send in one route, how can I append them all into one and send the accumulated list at the end?
I prefer to do it in the following form:
{
"writer": {success message},
"archive": {success message},
...
}
and another one like above for the list errors.
here is the code:
router.post('/some/route', function (req, res) {
if (req.isLoggedIn()) {
return res.status(403).json({});
}
MyModel.findById(req.user._id,function (err, data) {
if(err || data.rights !== 'super'){
return res.status(403).json({});
}
if(req.body.writer){
Books.update(
{ writer : req.body.id},
{ $set : { writer : req.body.writer} },
function (err) {
if(err){
res.status(500).send(err);
}
else{
res.status(200).send('updated successfully.');
}
}
);
}else{
Books.remove({writer: req.body.id}, function(err){
if (err){ return console.log(err)}
});
}
MetaInfo.findOneAndRemove({_id: req.body.id}, function (err, data) {
console.log(err);
});
Archive.findOne({_id: req.body.id},function (err, data) {
smtpTransporter.sendMail({...}, function (error, response) {
if (error) {
console.log(error);
} else {
console.log("Mail sent");
}
smtpTransporter.close();
});
data.remove();
if (err) {
console.log(err);
return res.status(200).json({
success: false,
message: 'server error',
err: err
});
}
res.status(200).json({
success: true
});
})
});
});
I assume your problem are the asynchronous calls to the database.
So best take a library of your choice (for example async) and do your async processes, in the callback then finally send your result.
Your result could look like this:
async.parallel([
function(callback) { ... },
function(callback) { ... }
], function(err, results) {
// send your result here
});
Note that if you are using .parallel the final callback will be immediatly called if one of the promises fails. see the docu

Callback/Promises implementation for a boolean check

Currently I have the following callback system:
var saveTask = function(err, result) {
if (err) return callback(err, result);
var newid = mongoose.Types.ObjectId();
var task = new Task({
_id: newid,
taskname: req.body.name,
teamid: req.body.team,
content: req.body.content,
creator: req.user.userId
});
task.save(function (err) {
if (!err) {
log.info("New task created with id: %s", task._id);
return callback(null, task);
} else {
if(err.name === 'ValidationError') {
return callback('400', 'Validation error');
} else {
return callback('500', 'Server error');
}
log.error('Internal error(%d): %s', res.statusCode, err.message);
}
});
};
if (req.body.team) {
valTeam.isMember(req.body.team, req.user._id, function (err, done) {
if (err) {
saveTask('403', 'Not the owner or member of this team');
} else {
saveTask(null, true);
}
});
} else {
saveTask(null, true);
}
valTeam.isMember
exports.isMember = function(teamid, userid, callback) {
Team.find({'_id':teamid, $or:[{'creator': userid }, {'userlist': { $in : [userid]}}]}, function(err, result) {
if (err) return err;
console.log(result);
if (!result.length)
return callback('404', false);
else
return callback(null, true);
});
}
In short, if team is sent by POST, I'm checking if the user is member of that ID in valTeam.isMember. Am I using the correct syntax and best method to call back my saveTask function to save the task if the user is part of the team?
This code currently works, but I feel like there should be an easier way to do it? How could I use a promise to achieve the same thing?
Thanks in advance.
It's curious the fact that you create objects instead Schemas. However "every head is a different world", this is my way:
task.save(function(error, data){
if (error) {
trow error;
} else {
//Make whatever you want here with data
});

Best way to handle the error in async node

To catch errors I have written if-else blocks in every function which looks bad. Please suggest a better way to handle errors in async node
async.waterfall([
function(callback){
fnOne.GetOne(req, res,function(err,result) {
if(err){
console.error("Controller : fnOne",err);
callback(err,null);
}
else{
var fnOne = result;
callback(null, fnOne);
}
})
},
function(fnOne, callback){
fnTwo.two(fnOne,function(err,result) {
if(err) {
console.error(err);
callback(err,null);
}
else{
callback(null, context);
}
})
}
], function (err, result) {
if(err){
console.error("Controller waterfall Error" , err);
res.send("Error in serving request.");
}
});
You can pass the error to async and catch it in the callback
async.waterfall([
function (callback) {
fnOne.GetOne(req, res, callback); // err and result is passed in callback
}, // as it's "function(err, result)"
function (fnOne, callback) { // the same as the arguments for the
fnTwo.two(fnOne, callback); // callback function
}
], function (err, result) {
if (err) {
console.error("Error :", err);
res.send("Error in serving request.");
}else{
res.end("A-OK");
}
});
You do too much stuff
Waterfall already have an internal error management.
callback(err, [results]) - An optional callback to run once all the
functions have completed. This will be passed the results of the last
task's callback.
Try this
async.waterfall([
function(callback){
fnOne.GetOne(req,res, callback)
},
function(fnOne, callback){
fnTwo.two(fnOne,callback) {
}
], function (err, result) {
if(err){
console.error("Controller waterfall Error" , err);
res.send("Error in serving request.");
}
});
async.each(files, (file, callback) => {
// Create a new blob in the bucket and upload the file data.
const blob = bucket.file(file.file.originalname);
const blobStream = blob.createWriteStream();
blobStream.on('error', (err) => {
callback(err);
});
blobStream.on('finish', () => {
// The public URL can be used to directly access the file via HTTP.
Storage.bucket(BUCKET_NAME)
.file(blob.name)
.move(body.email + '_' + file.dir + '.' + blob.name.split('.').pop())
.then((e) => {
body[file.dir] = format(`https://storage.googleapis.com/${BUCKET_NAME}/${e[0].name}`)
callback();
})
.catch(err => {
console.error('ERROR: ', err);
});
});
blobStream.end(file.file.buffer);
}, (err) => {
if (err) {
console.error(err);
return res.status(422).send({error: true, data: {message: "An error occured. Please fill all fields and try again"}});
}
// save to db
});

Categories