Nodejs + Express - Cannot call method 'json' of undefined - node.js

I'm working with Express, Hoganjs, Nodejs.
The code below keeps returning "500 TypeError: Cannot call method 'json' of undefined"
I figure there is something wrong with how I am calling getUsers, but I'm too much of a newbie to figure it out. None of the tutorials or forums I have referred to are helping as of yet.
exports.admin = function(req, res){
getUsers(function(error, res){
res.render('admin', {
title: 'Admin',
userlist: res
});
});
};
function getUsers(req, res) {
res.json([
{username: "fake", permission: "admin" },
{username: "fake2", permission: "user" }
])
}
exports.getUsers = getUsers;

OK, so your code snippet is not properly formatted and thus hard to read, but I think exports.admin is being called as an express route that takes req, res. So far so good. Then you call getUsers and pass it a callback, but getUsers doesn't take a callback, it takes req, res as well. It's unclear what you're trying to do, but something like this would be reasonable:
exports.admin = function(req, res) {
loadUsers(function (error, users) {
if (error) {
return res.status(500).send(error);
}
res.render('admin', {
title: 'Admin',
userlist: users
});
}
};
exports.getUsers = function(req, res) {
loadUsers(function (error, users) {
if (error) {
return res.status(500).send(error);
}
res.send(users);
});
}
function loadUsers(callback) {
process.nextTick(function () {
callback(null, [
{username: "fake", permission: "admin" },
{username: "fake2", permission: "user" }]
);
});
}
An even better pattern would be to code loadUsers as middleware, FYI.

Related

How to pass Data Between multiple routes in express?

app.post("/login", passport.authenticate("local",), function (req, res) {
const user = new Model({
username: req.body.username,
password: req.body.password,
});
req.login(user, function (err) {
if (err) {
console.log("wrong password");
} else {
passport.authenticate("local")(req, res, function () {
res.redirect("/admin");
});
}
});
});
app.post("/admin", function (req, res) {
Model.findOne({username: "siddharth"}).exec(function(err, foundList){
if(foundList)
{
const list = new linkModel({
linkTitle: req.body.linkTitle,
linkUrl: req.body.linkUrl,
});
foundList.links.push(list);
foundList.save();
res.redirect("/admin");
}
else
{res.send("redirect to home page and then login");
res.redirect("/login");
}
});
});
How can i pass the username when authenticated from login routeto other route(admin) where mongoose query is defined findone?
As i have defined it explicitly.
Or i simple terms how can i pass the data among routes ?
You can't. Instead use a middleware to do the checks you want and pass on the result to another middleware or return the response in case of error.

Err: Cannot POST /api/success

I am trying to send a POST request but it seems like the route isn't working based off the error. However, I get the value of req.body.entries logged in my console so it has to be working. When I send a GET request the browser sits and loads. The same thing happens in Postman. I am trying to figure out why I get an error on the POST request and the GET request sits. Thank you in advance.
index.js
const successRoute = require("./routes/success");
app.use("/api/success", successRoute);
success.js
router.get(
"/",
basicAuth({
users: { username: "password" },
}),
async function (req, res) {
try {
const entry = await Entry.find({});
res.sendStatus(entry);
} catch (err) {
res.sendStatus({ msg: "oops something went wrong" });
}
}
);
router.post("/", async (req, res, next) => {
try {
//console.log(req.body);
const { store, entries } = req.body;
Entry.findOneAndUpdate(
{ _id: store },
{ $set: { entries: +req.body.entries } },
{ new: true },
(err, doc) => {
if (err) {
console.log(err);
}
console.log(req.body.entries);
next();
}
);
} catch (err) {
const entry = new Entry({
_id: store,
entries: req.body.entries,
});
await entry.save();
res.sendStatus(200);
next();
}
});
I think the problem is, inside your router.post(), you're not sending any response, you only call next(). I don't see any next route for the request.
I'd recommend something like this:
router.post("/", async (req, res, next) => {
try {
//console.log(req.body);
const { store, entries } = req.body;
Entry.findOneAndUpdate(
{ _id: store },
{ $set: { entries: +req.body.entries } },
{ new: true },
(err, doc) => {
// throw the error and catch later
if (err)
throw err;
console.log(req.body.entries);
// send response instead of calling next()
res.sendStatus(200);
}
);
} catch (err) {
const entry = new Entry({
_id: store,
entries: req.body.entries,
});
await entry.save();
// send response
res.sendStatus(200);
// next(); // don't call next unless next route defined
}
});

Can i call a function from inside mustache templates which takes parameters?

I have a function defined in user_helper.js which generally returns value.
When I call getUserInfo() (in user_helper.js) from mustache template, it normally returns a String. But when I want results from mongoose query it returns NULL and not even shows any error.
index.js
router.get("/home", (req, res) => {
Profile.find()
.then(profile => {
res.render("index", {
userID: req.session.userID,
helper: require("../helpers/user_helper")
});
})
.catch(err => {
res.status(400).send("Unable to fatch");
});
});
module.exports = router;
user_helpert.js
var users = {
getUserInfo: function() {
return function(userID, render) {
var query = {_id:userID};
User.findOne(query, function(error, user) {
// Inside, return nothing
if (error) {
return error;
} else {
return user;
}
});
return "Some Text"; //This one's return result
};
}
};
module.exports = users;
mustache template
{{#helper.userInfo}}{{ userID }}{{/helper.userInfo}}
It should returns user information from database.
Can anyone knows about this or any better approach ?
There are many issues in your function.
The first one is that the query variable does not exist in the getUserInfo function. So the User.findOne() can't be executed.
The second error is that the User.findOne() method is asynchronous, so you won't be able to return a value as expected.
You should modify your /home route in the index.js to retrieve the user:
router.get("/home", (req, res) => {
User.findOneById(req.session.userID)
.then(user => {
res.render("index", {
user: user
});
})
.catch(err => {
res.status(400).send("Unable to fetch");
});
});
And in your template:
{{user.id}}

Returning an Object from middleware function in Node.JS

I am new to Node.JS coming from a Java Background I am using express to build this Rest API . What I am trying to do is build the concept of a manager. I am looking for a elegant way of returning a user object in the following:
users route: user.js
router.get('/find/:email', function(req, res, next){
userWare.findUserByEmail(req, res, next)
});
middleware/manager: usermiddleware.js
module.exports = {
findUserByEmail: function(req, res, next) {
models.core_user.find({
where:{
email: req.params.email
}
}).then(function(user){
res.json(user)
}, function(err){
res.status(404).json(err);
});
},
}
So In this above function I would like to return the user object to the route instead of the json. so that I can create the json from the object in the route. The whole point of this manager class will be to fectch and return objects.
What you need to do is call the callback function with the data you need or return the promise.
Callback
user.js
router.get('/find/:email', function (req, res, next) {
userWare.findUserByEmail(req.params.email, function (err, data) {
// error as first parameter or null if no error occurred
if (err) {
return res.status(404).json(err);
}
res.json(user);
});
});
usermiddleware.js
module.exports = {
findUserByEmail: function (email, next) {
models.core_user.find({
where: {
email: email
}
}).then(
function (user) {
// async call of callback with user object
next(null, user);
},
function (err) {
// async call of callback with error
next(err);
}
);
}
};
Promise
You could also just return the promise returned by your model, then it would look like this:
user.js
router.get('/find/:email', function (req, res, next) {
userWare.findUserByEmail(req.params.email).then(
function (user) {
res.json(user);
},
function (err) {
res.status(404).json(err)
}
);
});
usermiddleware.js
module.exports = {
findUserByEmail: function (email) {
// return the promise to the caller
return models.core_user.find({
where: {
email: email
}
});
}
};

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