Multiple callbacks in express - node.js

I have 2 schemas : Blogdemo and Review. Both in the same file : landing.ejs
I want both the content of both the schemas to appear on the landing page.
Code :
app.get('/', function (req, res,next) {
Blogdemo.find({}).sort([['_id', -1]]).limit(3).exec(function(err,allBlogs) { //finds the latest blog posts (upto 3)
if(err) {
console.log(err);
} else {
res.render("landing", {blog : allBlogs , moment : now});
}
})
next();
}, function (req, res) {
Review.find({}).sort([['_id', -1]]).limit(3).exec(function(err,allReviews) { //finds the latest reviews (upto 3)
if(err) {
console.log(err);
} else {
res.render("landing", {review : allReviews, moment : now});
}
})
})
I get the error : "review is not defined". If I change the order of the callbacks, I get the error : "blog is not defined". I understand that something is wrong with the callbacks.
I browsed through the express docs and used this :
app.get('/', function (req, res, next) {
console.log('Request URL:', req.originalUrl)
next()
}, function (req, res, next) {
console.log('Request Type:', req.method)
next()
})
This works perfectly.But I'm following the exact pattern and it's not working. What am I doing wrong?

From what I can see you are doing one thing wrong: placing next() outside the async scope.
Try this:
app.get('/', function(req, res, next) {
Blogdemo.find({}).sort([
['_id', -1]
]).limit(3).exec(function(err, allBlogs) { //finds the latest blog posts (upto 3)
if (err) next(err);
res.locals.blog = allBlogs;
res.locals.moment = now;
next();
})
}, function(req, res) {
Review.find({}).sort([
['_id', -1]
]).limit(3).exec(function(err, allReviews) { //finds the latest reviews (upto 3)
if (err) return next(err);
res.locals.review = allReviews;
res.render("landing", res.locals); // 2nd argument is optional.
})
})

I think, what you are doing wrong is rendering same page 2 times with res.render and passing one of the two variables each time, that's why the other one is coming as undefined.
You should render the landing page in the last callback, and just pass variables from first callback to second.
To pass variables from one callback to another, you can use res.locals.
Also, you should put next() inside the callback of find. so that it passes the variable to next callback correctly.
Try this:
app.get('/', function (req, res,next) {
Blogdemo.find({}).sort([['_id', -1]]).limit(3).exec(function(err,allBlogs) { //finds the latest blog posts (upto 3)
if(err) {
console.log(err);
next();
} else {
//set variable in `res.locals` and pass to next callback
res.locals.blog = allBlogs;
next();
}
})
}, function (req, res) {
Review.find({}).sort([['_id', -1]]).limit(3).exec(function(err,allReviews) { //finds the latest reviews (upto 3)
if(err) {
console.log(err);
} else {
allBlogs = res.locals.blog;
res.render("landing", {blog : allBlogs ,review : allReviews, moment : now});
}
})
})
For more information on res.locals read Express res.locals documentation.
Hope this helps you!

Related

why am i getting this error when i try to req.logout()?

Error which i am getting:
Error: req#logout requires a callback function
My code:
// #desc Logout User
// #rote GET /auth/logout
router.get("/logout", (req, res)=>{
req.logout()
res.redirect("/")
})
req.logout() is an asynchronous function (it was not this way before, they only introduced this change recently for security reasons), and your current code is synchronous, which is why you get this error.
You can fix this error by modofying your code as follows:
app.post('/logout', function(req, res, next) {
req.logout(function(err) {
if (err) { return next(err); }
res.redirect('/');
});
});
This is a change introduced in recent release. You can find it here in detail https://medium.com/passportjs/fixing-session-fixation-b2b68619c51d
From the documentation https://www.passportjs.org/concepts/authentication/logout/
try below code
router.get("/logout", function(req, res, next) {
req.logout(function(err) {
if (err) {
return next(err);
}
res.redirect("/");
});
});

mongoose not returning posts when *.find() is wrapped in a function

i have setup a simple express rest api and i want to get all posts when a certain route is requested from my rest server ("/api/posts")
i tried to console.log(posts) and it shows me the posts but when i try to go to the route http://localhost:5000/api/posts it shows me a blank page although console.logging the posts shows me the posts why is this
function getAllPosts() {
Post.find((err, posts) => {
if (err) {
console.error(err);
}
console.log(posts);
console.log("all posts requested");
return posts;
});
}
router
.route("/")
.get((req, res) => {
let posts = getAllPosts();
res.send(posts);
})
i expected to get the posts json when i go to http://localhost:5000/api/posts
when you are returning value from the getAllpost functions, routes are getting undefined value because of async. Try to edit your code like this:
function getAllPosts(req, res) {
Post.find((err, posts) => {
if (err) {
console.error(err);
}
console.log(posts);
console.log("all posts requested");
res.send( { data: posts} );
});
}
router
.route("/").get((req, res) => {
getAllPosts(req, res);
});
This has to do with the asynchronous nature of the code you have here. res.send is executing before Post.find() can resolve. You only want to res.send(posts) once the database query has finished fetching the data, if using callbacks, as you are in your example, you would do this inside of the callback. You could do something like this.
router
.route("/")
.get((req, res) => {
Post.find({}, (err, posts) => {
if (err) {
return res.status(500).send(err)
}
res.send(posts)
})
})

ExpressJS Mongoose render view after getting data from database

I need to render view after mongodb provide data from database. I write this code but the main problem is view is generated before getting data.
router.get('/', async(req, res, next)=> {
try{
const products = await Product.find({});
res.render('shop/index', { title: 'Express',products });
}catch(e){
console.log(e);
}
});
You could try doing something like this:
router.get("/", async (req, res, next) => {
Product.find({}, (err, items) => {
if (err) {
console.log(err);
res.status(500).send("An error occurred", err);
} else {
res.render("home", { items });
}
});
});
Actually everything looks great, idk why it's not working. I probably write something like this:
router.get("/", async (req, res) => {
const products = await Product.find({});
if (!products) return res.render("error");
res.render("shop/index", {
title: "Express",
products
});
});

req.params.id is undefined, I can't figure out why

Here is the url in browser:
http://localhost:3001/user/59cc018171149931f4e435ac
This is the code for the route:
router.get("/new", middleware.userLoggedIn, function(req, res){
console.log(req.params.id);
//find user by id
User.findById(req.user._id, function(err, foundUser){
if(err) {
console.log(err);
} else {
res.render("characters/addCharacter", {user: foundUser});
}
});
});
This is the middleware:
middlewareObj.userLoggedIn = function(req, res, next) {
// eval(require('locus'));
if (req.isAuthenticated() && req.params.id.equals(req.user._id)) {
return next();
}
res.redirect("/login");
};`
When I run the app everything works fine. Expect the request on the route above, wich giving me the error "TypeError: Cannot read property 'equals' of undefined". When I take off the middleware and try to console.log(req.params.id), it returns "undefined". What am I doing wrong?
Edit:
I have my route configured in app.js:
app.use("/user/:id/characters", characterRoutes);
So the ":id" is there.
When i use /new/:id instead of /new geting new error message:
"Cannot GET /user/59c23b864262fc2c186677be/characters/new"
use /new/:id instead of /new
router.get("/new/:id", middleware.userLoggedIn, function(req, res){
console.log(req.params.id);
//find user by id
User.findById(req.user._id, function(err, foundUser){
if(err) {
console.log(err);
} else {
res.render("characters/addCharacter", {user: foundUser});
}
});
});
You have to specify the parameter you want in your path, like so /new/:id and then you can use req.params.id in your code.
Here's the Express documentation for this part
cast req.params into any first:
let params: any = req.params
and then params.id should work

Node.js / Express.js Removing user from request, removes it from database

Here is the code:
exports.delete = function (req, res, next) {
console.log(req.user);
req.user.remove(function (err) {
if(err) {
return next(err);
} else {
res.json(req.user);
}
})
};
Of course this function is callback of delete method, what I don't understand is that, why removing req.user also deletes the specific user from MongoDB, as it is just a request.
Edit:
I have another callback(GET) which is executed on the same route:
exports.userByID = function (req, res, next, id) {
User.findOne({
_id: id
}, function (err, user) {
if (err) {
return next(err);
} else {
req.user = user;
next();
}
});
};
User is MongoDB model.
Where you do your req.user = user you're setting the value of req.user to the instance of your mongodb model.
So, calling req.user.remove is in fact calling your mongodb model remove function.
Change your delete function to:
exports.delete = function (req, res, next) {
console.log(req.user);
delete req.user
//etc...
};
delete req.user will remove the user object from your request object

Resources