How to pass parameters to nested controller in express.js? - node.js

I have a router like that:
app.get('/rest/userList', (req, res) => {
UserList.find({}, (err, users) => {
if (err) res.send(err);
res.json(users);
});
});
I would like to change it like this:
app.get('/rest/userList', getUsers);
function getUsers(req, res) {
UserList.find({}, createResponse);
}
function createResponse(err, users) {
if (err) return res.send(err);
return res.send(users);
}
However; in createResponse function 'res' is undefined. How can I do that?

You can use .bind() to bind extra parameters to the callback:
app.get('/rest/userList', getUsers);
function getUsers(req, res) {
UserList.find({}, createResponse.bind(null, res));
}
function createResponse(res, err, users) {
if (err) return res.send(err);
return res.send(users);
}

app.get('/rest/userList', getUsers, createResponse);
function getUsers(req, res, next) {
UserList.find({}, function (err, users) {
if (err) {
req.errr = err;
} else {
req.users = users;
}
next();
});
}
function createResponse(req, res) {
if (req.err) return res.send(req.err);
return res.send(req.users);
}

Related

is there any way of accessing a variable inside one express route inside another different route?

i have this project where im populating a lists collection in database with todos in the show page of a 'board'.
This is the board showpage route
app.get('/boards/:id', (req, res) => {
Board.findById(req.params.id).populate('lists').exec(function(err, foundBoard) {
if (err) {
console.log(err.message);
} else {
List.findById(req.params.id).populate("todos").exec(function (err, foundlist) {
if (err) {
console.log(err);
} else {
console.log(foundlist);
res.render('show', { board: populatedboard ,list:foundlist});
}
});
}
});
});
and this is the route where the variable is i want to access.i want to access and use that variable in the board show route (const listid is the variable i want to use in the board show route)
app.post("/boards/:id/lists/:listid/todos",function(req,res){
Board.findById(req.params.id, function (err, foundBoard) {
if (err) {
console.log(err);
} else {
Todo.create(req.body.todo, function (err, newtodo) {
if (err) {
console.log(err);
} else {
console.log(newtodo);
List.findById(req.params.listid, function (err, foundlist) {
if (err) {
console.log(err)
} else {
**const listid**= req.params.listid;
foundBoard.todos.push(newtodo);
foundBoard.save();
res.redirect("/boards/" + foundBoard._id);
}
});
}
});
}
});
})

Node/Express render multiple postgres queries on same html file

I have a query that returns results from my database, but I don't see how I can have it give me results from multiple queries.
router.get("/", function(req, res) {
pg.query("SELECT * from tic", (err, done) => {
if (err) {
console.log(err);
}
res.render("index", { tic: done.rows });
});
});
I was trying along the lines of this, but cant get it to work since he render statement is inside the query and I can get the render to see the results when I move it out of there
router.get("/", function(req, res) {
pg.query("SELECT * from tic", (err, tic) => {
if (err) {
console.log(err);
}
pg.query("SELECT * from tac", (err, tac) => {
if (err) {
console.log(err);
}
});
res.render("index", { tic: tic.rows }, { tac: tac.rows});
});
You just have to render after the second query executes. To do that you have to render in the callback of the second query.
router.get("/", function (req, res) {
pg.query("SELECT * from tic", (err, tic) => {
if (err) {
console.log(err);
}
pg.query("SELECT * from tac", (err, tac) => {
if (err) {
console.log(err);
}
res.render("index", { tic: tic.rows }, { tac: tac.rows });
});
});
});

How to reuse database controllers

I'm trying to reuse my controllers which handle database operations. I'm bit struggling with structuring my application. Here's what I have:
server.js
var apiController = require('./controllers/api');
router.get('/cars', function (req, res) {
// get all cars from DB and render view
apiController.getCars().then(function (cars) {
res.render('index', {cars: cars});
});
});
router.get('/api/cars', function (req, res) {
// get all cars from DB and return JSON
apiController.getCars().then(function (cars) {
res.json(cars);
});
});
controllers/api.js
module.exports = {
getCars: function () {
db.collection('cars').find().toArray( function (err, cars) {
if (err) throw err;
return cars;
});
},
// tried also something like this but this doesn't really work
// for my use case because I don't want to attach any particular
// res to the function
getCars: function (req, res, next) {
db.collection('cars').find().toArray( function (err, cars) {
if (err) throw err;
res.json(cars);
});
},
};
Your current problem is that you expect promises as return in server.js while you use callbacks in the controller. I suggest you change your function getCars to return a Promise. Don't know what ODM/ORM you're using but it might look like something like this:
getCars: function () {
return db.collection('cars').find();
},
server.js
var apiController = require('./controllers/api');
router.get('/cars', function (req, res) {
apiController.get('cars').then(function (cars) {
res.render('index', {cars: cars});
});
});
router.get('/api/cars', function (req, res) {
apiController.get('cars').then(function (cars) {
res.json(cars);
});
});
controllers/api.js
var Promise = require('bluebird');
module.exports = {
get: function (modelName) {
return new Promise(function(resolve,reject){
return db.collection(modelName).find().toArray(function(err, models){
if (err) {
return reject(err);
}
else {
return resolve(models);
}
});
});
}
};
server.js
var apiController = require('./controllers/api');
router.get('/cars', apiController.getCars);
controllers/api.js
function getCarsAsync(req, res, next){
db.collection('cars').find().then(function(carsData){
if(carsData){
return res.send(carsData);
}
else{
return res.status(401).send('User is not authorized');
}
}).catch(function(err){
return next(err);
});
}
module.exports = {
getCars: getCarsAsync
};

Custom callback in Express.js get

I have a get in my app.js
app.get('/api/personnel', api.personnel);
that calls this function as a callback to load some data from mongo:
exports.personnel = function(req, res) {
var docs;
db.personnel.find(function(err, docs) {
if (err) {
logError(err);
} else {
res.json({
personnel: docs
});
}
});
};
That works just fine, but I'd really like to be able to call a callback for testing purposes when the function is complete:
exports.personnel = function(req, res, callback) {
var docs;
db.personnel.find(function(err, docs) {
if (err) {
logError(err);
} else {
res.json({
personnel: docs
});
}
callback();
});
callback() is empty when the function is called from the live application and gives me a error:
Error: Can't set headers after they are sent.
How do I go about having a get call my callback?
You can just wrap that function to insert the additional function argument:
exports.personnel = function(req, res, callback) {
var docs;
db.personnel.find(function(err, docs) {
if (err) {
logError(err);
} else {
res.json({
personnel: docs
});
}
});
///////////////////////////////////////////////////
var callback = ...;
pp.get('/api/personnel', function(req, res) {
api.personnel(req, res, callback);
});
third arity in Express is always reserved for next() callback (as found in middlewares).
If you want to have "callback" but does not want to mess up with express, let's hack!
exports.personnel = function(req, res, callback) {
var docs;
db.personnel.find(function(err, docs) {
if (err) {
logError(err);
} else {
res.json({
personnel: docs
});
}
if(process.env.NODE_ENV === 'test')
callback();
});
then, when you want to test, export NODE_ENV=test in your shell

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