Mongoose not picking up on callback variables for render - node.js

app.get("/users/:id", function (req, res) {
User.findById(req.params.id, function (err, foundUser) {
if (err) {
console.log(err)
} else {
console.log(foundUser)
}
})
Item.countDocuments({ UID: req.params.id }, function (err, itemCount) {
if (err) {
console.log(err)
} else {
console.log(itemCount)
}
})
Item.find({ UID: req.params.id }, function (err, foundItems) {
if (err) {
console.log(err)
} else {
console.log(foundItems)
}
})
res.render("users/show", { user: foundUser, newListItems: foundItems, itemCount: itemCount})
For some reason this wont render and keeps saying that the variables dont exist despite the callbacks above. Using EJS for render.

I may be reading the code incorrectly, but aren't the callback variables out of scope for the res.render method?
You may need to return the callbacks from each of the Mongoose queries and store them in variables that are within the scope of the res.render method.

As suggested by Richie, you cannot access the variables from.inside the callbacks in the outer scope
app.get("/users/:id", function (req, res) {
User.findById(req.params.id, function (err, foundUser) {
if (err) { console.log(err) }
else {
console.log(foundUser)
Item.countDocuments({ UID: req.params.id }, function (err, itemCount) {
if (err) { console.log(err) }
else {
console.log(itemCount)
Item.find({ UID: req.params.id }, function (err, foundItems){
if (err) { console.log(err) }
else {
console.log(foundItems);
res.render("users/show", { user: foundUser, newListItems: foundItems, itemCount: itemCount})
}
});
}
});
}
});
});
Notice that I've put the res.render method inside the callbacks so that the variables are available to it.
EDIT
As suggested by Marcel Djaman, You should probably use async/await to make code more readable. Read more about async/await here
app.get("/users/:id", async function (req, res) {
try {
const foundUser = await User.findById(req.params.id);
console.log(foundUser)
const itemCount = await Item.countDocuments({ UID: req.params.id });
console.log(itemCount)
const foundItems = await Item.find({ UID: req.params.id });
console.log(foundItems);
res.render("users/show", { user: foundUser, newListItems: foundItems, itemCount: itemCount});
} catch(err) {
console.error(err)
}
});
You can notice how much simpler this code is than the one above it.

Related

Update database

I am struggling to get .put or .patch to work. when using postman I get the call back returned but the values are not updated in my database on robo 3t. I have tried fixing the deprecation warning and using updateOne, updateMany.
This will fix the deprecation warning but will not update the database. Here is the code before i fix the deprecation. Any ideas what I'm doing wrong?
////////////////////Request Targeting A Specific Article///////////////////////
app.route("/articles/:articleTitle")
.get(function(req, res){
Article.findOne({title: req.params.articleTitle}, function(err, foundArticle){
if (foundArticle) {
res.send(foundArticle);
} else {
res.send("No articles with that title.");
}
});
})
/////////PUT PROBLEM MUST BE FIXED /////////////
.put(function(req, res){
Article.update(
{title: req.params.articleTitle},
{title: req.body.title, content: req.body.content},
{overwrite: true},
function(err){
if(!err){
res.send("succesfully updated");
}
}
);
})
///////PATCH PROBLEM MUST BE FIXED ///////////
.patch(function(req, res){
Article.update(
{title: req.params.articleTitle},
{$set: req.body},
function(err){
if(!err){
res.send("Successfully updated article.");
} else{
res.send(err);
}
}
);
});
app.route("/articles/:articleTitle")
.get((req, res) => {
Article.findOne({ title: req.params.articleTitle }, (err, result) => {
if (result) {
res.send(result);
} else {
res.send("err");
}
});
})
.put((req, res) => {
Article.replaceOne(
{ title: req.params.articleTitle },
{ title: req.body.title, content: req.body.content },
{ overwrite: true },
(err) => {
if (err) {
res.send("There is some error");
} else {
res.send("Updated successfully");
}
}
);
})
.patch((req, res) => {
Article.updateOne(
{ title: req.params.articleTitle },
{ $set: req.body },
(err) => {
if (err) {
res.send("There is some error");
} else {
res.send("Updated successfully");
}
}
);
});
Try this!! this will work fine.

nodejs how to do multiple requests

i need to know how i can write my request to make multiple delete.
the second thing is how can i put async function on my code.
i want to delete a campus and in the same time dele the builings with the same id campus in the JSON
app.delete('/campuses/:id', (req, res)=> {
const id = req.params.id;
const details = { 'campusid': new ObjectID(id) };
db.db('').collection('buildings').remove(details, (err, result)=> {
if (err) {
res.send({ 'error': 'en error has occured' });
} else {
res.send(result);
}
});
const details2 = { '_id': new ObjectID(id) };
db.db('').collection('campuses').remove(details2, (err, result)=> {
if (err) {
res.send({ 'error': 'en error has occured' });
} else {
res.send(result);
}
}
);
})
You can delete like this.
app.delete('/campuses/:id', async (req, res)=> {
try {
const id = req.params.id;
const details = { 'campusid': new ObjectID(id) };
await db.db('').collection('buildings').remove(details);
const details2 = { '_id': new ObjectID(id) };
await db.db('').collection('campuses').remove();
res.send(result);
} catch(err) {
return res.json({
success: false,
message: 'error'
});
}
})
You could make sequential functions where the first one calls the second one. You could then pass on variables to the seconds function (ie. your campus ID).
It could look something like this:
const Query1 = (res, query) => {
const request = new sql.Request();
request.query(query, (err, result) => {
if (err) {
return res.json({
success: false,
message: 'error'
});
} else if (result.recordset[0]) {
let campusID = result.recordset;
Query2(res, campusID, query = 'SELECT bla bla')
}
})
}
const Query2 = (res, campusID, query) => {
const request = new sql.Request();
request.query(query, (err, result) => {
if (err) {
return res.json({
success: false,
message: 'error'
});
} else {
return res.json({
success: true
});
}
})
}
There are various ways to make async call.
You can use promises.
Async Functions.
Sending response without waiting for other tasks.
If you want to make parallel calls you can use bluebird join function
I like the syntax of async functions better than promises, but I use both depending on the situation.
Here is an example of running functions in order before moving to the next function:
async.waterfall([
function(callback1) {
//Do some work, then callback
if (error) {
callback1(errorGoesHere,null);
} else {
callback1(null,successMessageGoesHere);
}
},
function(callback2) {
//Do some work, then callback
if (error) {
callback2(errorGoesHere,null);
} else {
callback2(null,successMessageGoesHere);
}
}
], function (error, success) {
if (error) {
//show an error
}
//all good return the response, etc.
});
If anything in these functions fail, it automatically call the end function to catch the error.

how to get multiple collection data in single get method using mongoose in node js

router.post('/user', function (req, res) {
try {
MongoClient.connect("mongodb://test123:test123#localhost:27017/test", function (err, db) {
db.collection('user').findOne({ _id: req.body.UserId }, function (err, founddata) {
if (err) {
} else {
if (founddata) {
db.collection('project').find({ userId: founddata._id }, function (err, data) {
if (err) {
console.log(err);
} else {
console.log(data)
}
})
}
}
})
})
} catch (e) {
console.log(e)
}
})
I have tried many time but i geting error for second query is not run.output is null value.

how to use Promise with express in node.js?

I am using Promise with Express.
router.post('/Registration', function(req, res) {
var Promise = require('promise');
var errorsArr = [];
function username() {
console.log("1");
return new Promise(function(resolve, reject) {
User.findOne({ username: req.body.username }, function(err, user) {
if(err) {
reject(err)
} else {
console.log("2");
errorsArr.push({ msg: "Username already been taken." });
resolve(errorsArr);
}
});
});
}
var username = username();
console.log(errorsArr);
});
When I log errorsArray, it is empty and I don't know why. I am new in node.js. Thanks in advance.
Try the following, and after please read the following document https://www.promisejs.org/ to understand how the promises work.
var Promise = require('promise');
router.post('/Registration',function(req,res,next) {
function username() {
console.log("agyaaa");
return new Promise(function(resolve,reject) {
User.findOne({"username":req.body.username}, function(err,user) {
if (err) {
reject(err)
} else {
console.log("yaha b agyaaa");
var errorsArr = [];
errorsArr.push({"msg":"Username already been taken."});
resolve(errorsArr);
}
});
});
}
username().then(function(data) {
console.log(data);
next();
});
});
You can have other errors also (or things that shouldn't be done that way). I'm only showing you the basic use of a Promise.
router.post('/Registration', function(req, res) {
return User
.findOne({ username: req.body.username })
.then((user) => {
if (user) {
return console.log({ msg:"Username already been taken" });
}
return console.log({ msg: "Username available." });
})
.catch((err)=>{
return console.error(err);
});
});
you can write a clean code like this.
Promise is a global variable available you don't need to require it.

NodeJS - showing different content for logged in or not users

I'm trying to show defferent content for logged in and not users on one page.
Here is the code I use for generating / page:
app.get('/',function(req, res){
if (!checkSession(req, res)) {
res.render('index.ejs', {
title: 'FrontSpeak - blog-based social network'
})
} else {
res.render('index.ejs', {
title: 'autrhorized'
})
}
})
checkSession function:
function checkSession(req, res) {
if (req.session.user_id) {
db.collection('users', function (err, collection) {
collection.findOne({
_id: new ObjectID(req.session.user_id)
}, function (err, user) {
if (user) {
req.currentUser = user;
return true;
} else {
return false;
}
});
});
} else {
return false;
}
}
loggin function:
app.post('/', function(req, res){
db.collection("users", function (err, collection) {
collection.findOne({ username: req.body.username }, function (err, doc) {
if (doc && doc.password == req.body.password) {
console.log("user found");
req.session.user_id = doc._id;
}
}
});
});
});
So, it doesn't seems to be working. However, I think this is not the best way to display different content. May be there are some more elegant ways to do this? Thank you!
UPDATE: New login function:
app.post('/', function(req, res){
db.collection("users", function (err, collection) {
collection.findOne({ username: req.body.username }, function (err, doc) {
console.log('found user');
if (doc && doc.password == req.body.password) {
req.session.user_id = doc._id;
res.redirect('/');
};
res.redirect('/');
});
res.redirect('/');
});
});
This is a case of trying to apply the traditional synchronous model to Node's asynchronous callback-driven model.
After your database query completes, you return true, but you're just returning to the database driver. checkSession returned a long time ago. Since that function returns undefined if there is a session.user_id (and false if there isn't), the login check will always evaluate false.
Instead, you can use Brandon's suggestion to make checkSession asynchronous, or I recommend implementing a middleware function:
function checkLogin(req, res, next) {
if (req.session.user_id) {
db.collection('users', function (err, collection) {
if (err) return next(err); // handle errors!
collection.findOne({
_id: new ObjectID(req.session.user_id)
}, function (err, user) {
if (user) {
req.currentUser = user;
} else {
req.currentUser = null;
}
next();
});
});
} else {
req.currentUser = null;
next();
}
}
Now you have two ways of using your middleware function. If you want to check for a user on every request, just add it to the app:
app.use(checkLogin);
Now every single request will have a req.currentUser, but you incur the performance hit of fetching login state from the database for every request. Alternatively, if you only need user information for certain requests, stick the function in the route:
app.get('/', checkLogin, function(req, res) {
if (req.currentUser) {
// logged in
} else {
// not
}
});
You can read more about this in the Express docs.
It looks like you're trying to use checkSession as a synchronous function by checking its return value, but checkSession cannot be synchronous because it depends on asynchronous functionality, namely the callback here: db.collection('users', function (err, collection) .... You'll need to modify checkSession to be async:
function checkSession(req, res, callback) {
if (req.session.user_id) {
db.collection('users', function (err, collection) {
collection.findOne({
_id: new ObjectID(req.session.user_id)
}, function (err, user) {
if (user) {
req.currentUser = user;
callback(true);
} else {
callback(false);
}
});
});
} else {
callback(false);
}
}
and then use it asynchronously in your request handler:
app.get('/',function(req, res){
checkSession(req, res, function(isUser) {
if (!isUser) {
res.render('index.ejs', {
title: 'FrontSpeak - blog-based social network'
})
} else {
res.render('index.ejs', {
title: 'autrhorized'
})
}
});
})

Resources