How to create data and render within the one get request - node.js

dashboardRouter.route('/:Teachersubjects/:Class/:Salary')
.get(function(req,res)
{
function handleErr(err,redir)
{
if(redir){
res.end("err");
}
else{
res.writeHead(200, {"content-type": "text/plain"});
res.end('Error occured');
}
}
Verify.verifySchoolUser(req,res,function(err,schooluserId){
if(err){
res.redirect('/schoolusers/login');
return;
}
else{
SchoolUser.findOne({_id:schooluserId}).exec(function(err,schooluser){
if(err)
{
console.log("NOUSER school")
handleErr(err);
return;
}
else
{
if(schooluser.count<4){
console.log(req.params.Class);
vacancies.create(req.params,function(err,vacancy){
if(err){
console.log(err.message);
console.log(err.name);
console.log("create error");
for (field in err.errors) {
console.log(err.errors[field].message);
}
handleErr(err);
return;
}
console.log(vacancy);
schooluser.vacancies.push(vacancy._id);
schooluser.save(function(err){
if(err){
console.log(JSON.stringify(err));
handleErr(err);
}
teacherforms.find({}, function (err, teacherforms) {
if (err) throw err;
else {
var teacherforms = teacherforms;
console.log(teacherforms);
var arr=[];
for(i=0; i<teacherforms.length; i++ ) {
arr.push(teacherforms[i]._id);
console.log(arr);
}
teacherusers.find({checking:{$in:arr}}, function (err, teacherusers){
if (err) throw err;
else{
var data =[];
for(i=0; i<teacherusers.length; i++ ) {
var kdata={};
var str = "";
kdata.firstname = teacherusers[i].firstname;
kdata.profilepic= teacherusers[i].profilepic;
kdata.experience= teacherforms[i].Teachingexperience;
var len= teacherforms[i].Qualifications.length;
console.log(teacherforms[i].Qualifications);
for(j=len-1;j>=0;j--){
if(j==0){
str = str + teacherforms[i].Qualifications[j];
}
else{
str=str + teacherforms[i].Qualifications[j] + ", ";
}
}
kdata.qualifications = str;
data.push(kdata);
}
data.push(vacancy.Class);
//data.push(vacancy.Qualification);
data.push(vacancy.Teachersubjects);
console.log(data);
res.render('../views/dashboard/dashboard_resumes.ejs',{
sdata:data
});
}
});
}
})
});
})
}
else {
res.send("Limit Exceeded");
}
}
});
}
});
});
I want to save a object firstly in MongoDB at get request and then render a page in ejs with a new object.But I am getting a error 500(Internal Server Error),so can we save a object and render the ejs page with new object at same end point?
P.S- The code below is the crucial part of the code and there is no syntax error and schooluser(object) is passed from the upper part of the code.
dashboardRouter.route('/:Teachersubjects/:Class/:Salary')
.get(function(req, res) {
vacancies.create(req.params, function(err, vacancy) {
if (err) {
console.log(err.message);
console.log(err.name);
console.log("create error");
for (field in err.errors) {
console.log(err.errors[field].message);
}
handleErr(err);
return;
}
schooluser.vacancies.push(vacancy._id);
schooluser.save(function(err) {
if (err) {
handleErr(err);
} else {
res.render('../views/dashboard/dashboard_resumes.ejs', {
sdata: data
});
}
});
});
});

There are multiple issues with your route handler. First, whenever an error occurs you run this through your handleErr() function, but you never do anything with res in these cases. This means that the request is never resolved and eventually times out on the client side.
When an error occurs, you should respond with an error code. For instance:
res.status(400).send('Something went wrong');
When you actually do send a response and render the template, you're applying a variable called data. However, this variable doesn't seem to be declared anywhere in your code. Maybe you left out that code, but if that variable is indeed undeclared, it throws a ReferenceError which is caught by the routing framework which in turn responds with a 500 Internal Server Error. So this is probably the source of your issue.
Any uncaught errors thrown by vacancies.create() or schooluser.save() would have the same effect.

Related

Error: can't set headers after they are sent

I want to send an argument with res.redirect(). However, I'm getting an error while running it, saying that I cannot set headers after they are sent.
What does that mean, and how can I fix it?
app.post('/updateCollaborateRequest', function(req,res) {
if(req.body.accept == true) {
Team.findOne({'name': req.body.data.teamName}, function (err, team) {
if(err) {
res.redirect('/explore');
}
team.accepted = true;
team.save(function (err) {
if (err) {
alert(err);
}
Request.findOne({'emailAdmin': req.session.email}, function(err, request) {
request.seen = true;
request.save(function(err) {
if(err) {
console.log(err);
}
});
});
res.redirect("/teamprof/" + team.name);
});
});
}
Request.findOne({'emailAdmin': req.session.email}, function(err, request) {
request.seen = true;
request.save(function(err) {
if(err) {
console.log(err);
}
res.render('userprof1', {message : req.flash('done')});
});
});
});
Your code is continuing after redirecting. That is probably the problem. You should return, otherwise you are going to keep trying to write to the HTTP response.
This particular error message is caused by code paths that lead to multiple res.xxx() calls that try to send the response more than once.
You have multiple places where you are doing that. For example, you have two res.redirect() calls inside the Team.findOne() callback, but then you proceed with Request.findOne() where you have a res.render(). You HAVE to make sure that you only send the response once.
I'm not entirely sure what the desired logic is in all cases, but you can fix that error by adding an else statement before the Request.findOne() and adding a return after each res.redirect(). If this is not the exactly flow you want, then please explain more about how you want the control flow to work. Here's the code with those changes applied:
app.post('/updateCollaborateRequest', function(req,res) {
if(req.body.accept == true) {
Team.findOne({'name': req.body.data.teamName}, function (err, team) {
if(err) {
res.redirect('/explore');
return;
}
team.accepted = true;
team.save(function (err) {
if (err) {
// FIXME: need error handling here
alert(err);
}
Request.findOne({'emailAdmin': req.session.email}, function(err, request) {
request.seen = true;
request.save(function(err) {
if(err) {
// FIXME: need error handling here
console.log(err);
}
});
});
// Are you sure you want to send this response before
// you even know if the `Request.findOne()` and `request.save()`
// have been sucessful?
res.redirect("/teamprof/" + team.name);
return;
});
});
} else {
Request.findOne({'emailAdmin': req.session.email}, function(err, request) {
request.seen = true;
request.save(function(err) {
if(err) {
console.log(err);
}
res.render('userprof1', {message : req.flash('done')});
});
});
}
});
You still have several error conditions for which no response is sent which is incomplete error handling so those need to be fixed too. And, I've added some comments in the code about some other suspect things in the code.

Sending express response with data from 2 different mongoose queries

I need to query 2 different collections and send it in the express response. I have a very vague idea of what is needed to do so. I tried to contact the query documents to an empty array and send that new array as the response. But I receive an empty array as a response.
This is my route.
site.route('/campus/development')
.get(function(req, res) {
var devPosts = [];
development.find().exec(function(err, docs) {
if (err) {
console.log('Error : ' + err);
} else {
if (docs != null) {
devPosts = devPosts.concat(docs);
console.log(docs);
} else {
console.log('No posts found');
}
}
});
jobs.find().exec(function(err, jobs) {
if (err) {
console.log('Error : ' + err);
} else {
if (jobs != null) {
devPosts = devPosts.concat(jobs);
console.log(jobs);
} else {
console.log('No jobs');
}
}
});
res.send(devPosts);
});
This is due to the async operation of the requests to the database. There are a variety of solutions to this but basically distill down to two types: callbacks or promises.
A callback solution might look like:
site.route('/campus/development')
.get(function(req, res) {
development.find().exec(function(err, devDocs) {
if (err) {
console.log('Error : ' + err);
} else {
if (devDocs != null) {
console.log(devDocs);
jobs.find().exec(function(err, jobs) {
if (err) {
console.log('Error : ' + err);
} else {
if (jobs != null) {
console.log(jobs);
res.send([devDocs, jobs]);
} else {
console.log('No jobs');
}
}
});
} else {
console.log('No posts found');
}
}
});
});
But this introduces a couple of interesting issues: one is the phenomenon known as callback hell and the other is that you should be responding with the errors which means you would need to have a response call for each error (albeit this is a very simplistic approach to it).
As mentioned earlier there is another type of solution which involves using promises. There are a bunch of libraries that you can use and actually Mongoose returns a promise from the exec method. However if you are on Node 0.12.x you can also use the native Promise (it was introduced in 0.11 but you should be using 0.12.x over 0.11.x). A benefit to using the native promise over the one returned from Mongoose is that you can execute these requests in parallel since they don't depend on each other.
site.route('/campus/development')
.get(function(req, res) {
Promise.all([
development.find().exec(), // returns a promise
jobs.find().exec() // returns a promise
]).then(function(results) {
// results is [devDocs, jobs]
console.log(results);
res.send(results);
}).catch(function(err) {
res.send(err);
});
});

Mongoose .findOne not working as an internal function call

With this as a URL:
'api/support-tag/name/myTagName'
This function works properly:
getByName: function (req, res) {
model.Shared_SupportTag.findOne({name: req.params.name}).exec(function (err, results) {
if (err) {
return res.status(400).send({
message: errMsg.Util_ErrorMsg.getErrorMessage(err)
});
}
res.send(results);
})
}
But when I try to call a similar function from within the node server:
supportDoc.category = GetById(item.category);
function GetById(name){
model.Shared_SupportTag.findOne({name: name}).exec(function(err, result){
if(err){
console.log(err)
}else{
console.log(result);
}
})
}
The function does not execute, nor does the error catch, intellisense shows:
err= Reference error; err is not defined
result = Reference error; result is not defined
All I am trying to accomplish is a function call from within the server and not via a URL.
Any solution here? Thanks in advance
In the case of the findOne() method, the positive response (sans error) will either hold a mongoose object or null.
If the same query had been sent using just find(), the result would have been an empty array.
function GetById(name){
model.Shared_SupportTag.findOne({name: name}).exec(function(err, result){
if(err){
console.log(err)
}else{
if (result) console.log(result); //Check whether object exists.
else console.log('Not found!');
}
})
}
Solved:
model.Shared_SupportDoc.find({}).exec(function (err, collection) {
var supportDocs = require('../../data/_seed/support/supportDocs.json');
if (collection.length === 0) {
supportDocs.forEach(function (item) {
var supportDoc = new model.Shared_SupportDoc;
supportDoc.title = item.title;
supportDoc.created = item.date;
supportDoc.icon = item.icon;
supportDoc.likeCount = item.likeCount || 7;
-----> // requires callback - ie asynchronous
GetByName(item.category, function(tagId) {
supportDoc.categoryId = tagId;
-----> // must put save in the callback
supportDoc.save(function (err) {
if (err) {
console.log(supportDoc.categoryId)
console.log('Error: ' + err);
} else {
console.log('Support Doc Seed Complete');
}
});
});
})
}
});}
function GetByName(name, next) {
model.Shared_SupportTag.findOne({name : name}).exec(function (err, result) {
if (!result) {
console.log('Not Found');
next();
} else {
console.log(result._id);
next(result._id);
}
});}

Node.JS Restify and post Events

Hello I have a post event that takes in key value and maps it to a sproc. This part works very well. However, for some reason I cannot return the data response as JSON.
It says the return type is "octet stream" even though I'm setting it to application/json.
I'm a little new to REST, so if I'm doing something incorrectly, please let me know.
server.post({ path: PATH + '/data.json', version: '1.0' }, genericData);
function genericData(req, res, next) {
if (req.params.data != null){
var sp_code_name = config.get('data:' + req.params.data);
var connection = new sql.Connection(conn_str, function(err) {
// ... error checks
if(err) {
return console.log("Could not connect to sql: ", err);
connection.close();
}
});
var request = new sql.Request(connection);
console.log(sp_code_name);
request.execute(sp_code_name, function (err, recordset, returnValue) {
if (err) {
connection.close();
return console.log("Is this a good query or the right table?: ", err);
}
//if (recordset && returnValue == 0) {
res.setHeader('Access-Control-Allow-Origin','*');
res.setHeader('content-type', 'application/json');
res.send(200, recordset[0]);
console.log(recordset[0]);
// return next();
// }
// return next();
connection.close();
});
}
res.setHeader('content-type', 'application/text');
res.send(200, "Something is wrong with the stream.");
}
You need to wrap your error response in an else clause:
if (req.params.data != null){
...
} else {
res.setHeader('content-type', 'application/text');
res.send(200, "Something is wrong with the stream.");
}
Without the else clause, it's getting called on every request before your request.execute callback is called. That's setting your content-type to application/text instead of application/json as you're expecting.

nodejs usage of globals

I am new to NodeJS, but a pretty experienced programmer. I am working on a side project to get my feet wet with node. I am looking at MongoDB for storage. To get started I just wanted to test creating some basic CRUD functions in node. I have included my code below.
My question is regarding the global var 'updated'. It is used in the 'read' and 'update' functions to control which function gets called next. I would like to call them in the following order: create, read, update, read, delete.
It is my understanding that I cannot pass in a function parameter to 'read' to dictate which function to call next because this is called asynchronously and the lambda function I am defining as my callback to mongojs.read will not have the parent functions parameters to use.
What is the correct way to do this? Using a global feels like a hack. :)
// app.js
//<user>:<password>#<server>/<database>
var connection = "foo:bar#127.0.0.1/blah";
var collections = ["scores"]
var db = require("mongojs").connect(connection, collections);
//console.log(db);
var updated = false;
function my_create()
{
console.log("my_create");
db.leaderboard.save({name: "sunder", score: 42}, function(err, result) {
if(err || !result )
{
console.log("Score save failed");
console.log(err);
}
else
{
console.log("Score save successful");
my_read();
}
});
}
function my_read()
{
console.log("my_read");
// app.js
db.leaderboard.find({name: "sunder"}, function(err, results) {
if(err || !results)
{
console.log("Score read failed");
console.log(err);
}
else
{
results.forEach(function(score)
{
console.log(score);
});
// I am sure using globals is a HACK. Need to figure this out.
if(updated)
my_delete();
else
my_update();
}
});
}
function my_update()
{
console.log("my_update");
db.leaderboard.update({name: "sunder"}, {$set: {score: 1337}}, function(err, result) {
if(err || !result)
{
console.log("Score update failed");
}
else
{
console.log("Score update successful");
updated = true;
my_read();
}
});
}
function my_delete()
{
console.log("my_delete");
db.leaderboard.remove({name: "sunder"}, function(err, result) {
if(err || !result)
{
console.log("Score delete failed");
}
else
{
console.log("Score delete successful");
process.exit();
}
});
}
// kick off the flow of processes
my_create();
How about passing the next action to perform into the my_read() function like this:
// app.js
var connection = "foo:bar#127.0.0.1/blah";
var collections = ["scores"]
var db = require("mongojs").connect(connection, collections);
function my_create()
{
console.log("my_create");
db.leaderboard.save({name: "sunder", score: 42}, function(err, result) {
if(err || !result )
{
console.log("Score save failed");
console.log(err);
}
else
{
console.log("Score save successful");
my_read("update");
}
});
}
function my_read(next_action)
{
console.log("my_read");
// app.js
db.leaderboard.find({name: "sunder"}, function(err, results) {
if(err || !results)
{
console.log("Score read failed");
console.log(err);
}
else
{
results.forEach(function(score)
{
console.log(score);
});
// I am sure using globals is a HACK. Need to figure this out.
if(next_action === "delete")
my_delete();
else
my_update();
}
});
}
function my_update()
{
console.log("my_update");
db.leaderboard.update({name: "sunder"}, {$set: {score: 1337}}, function(err, result) {
if(err || !result)
{
console.log("Score update failed");
}
else
{
console.log("Score update successful");
updated = true;
my_read("delete");
}
});
}
function my_delete()
{
console.log("my_delete");
db.leaderboard.remove({name: "sunder"}, function(err, result) {
if(err || !result)
{
console.log("Score delete failed");
}
else
{
console.log("Score delete successful");
process.exit();
}
});
}
// kick off the flow of processes
my_create();

Resources