The app is using basic Node.js, Express, and MongoDB to return some data at different routes.
Route:
router.get('/api/v1/:collection', (req, res, next) => {
try {
const data = ApiController.getAllRecipes(req, res, next);
console.log(data);
res.send(data);
} catch(error) {
res.send(error);
}
});
The methods that query the database are contained within a simple controller:
module.exports = {
getAllRecipes: async (req, res, next) => {
try {
req.app.db.collection(req.params.collection).find({}).toArray((error, result) => {
if (result.length > 0) {
console.log(result);
return result;
} else {
let error = new Error();
error.statusCode = 404;
error.message = 'No API results found.';
next(error);
}
});
} catch (error) {
next(error);
}
}
};
Inside the controller, the console.log(result); returns the correct data that I would like to be handled within the route file. However, the console.log(data); in the Route file returns undefined, even within the try/catch block.
Am I missing something in passing the data from the controller back to the route? Thank you :)
Found a solution! Since the controller is asynchronous, it doesn't need to return data, but rather save it to res.locals and execute Express' next() method to continue with the route.
Updated Route:
router.get('/api/v:versionNumber/:collection', ApiController.getAllRecipes, (req, res, next) => {
res.send(res.locals.results);
});
Updated controller:
getAllRecipes: async (req, res, next) => {
try {
req.app.db.collection(req.params.collection).find({}).toArray((error, result) => {
if (result.length > 0) {
res.locals.results = result;
next();
} else {
let error = new Error();
error.statusCode = 404;
error.message = 'No API results found.';
next(error);
}
});
} catch (error) {
next(error);
}
}
Related
I'm developing a module which I'm using for passport authentication with ExpressJS, and I came up with this solution to gather all the passports methods I'm using:
// passport-controller-js
exports.signup = (passport) => (req, res, next) => {
// Authenticate methods ================
passport.authenticate('local-signup', function(err, user, info) {
if (err) {
return next(err); // will generate a 500 error
}
// Saving user...
return res.send({ success : true, message : 'signup succeeded' });
})(req, res, next);
};
exports.signin = (passport) => (req, res, next) => {
passport.authenticate('local-login', function(err, user, token, info) {
if (err) {
return next(err); // will generate a 500 error
}
req.login(user, loginErr => {
if (loginErr) {
return next(loginErr);
}
return res.send({ success : true, message : 'signin succeeded' });
});
})(req, res, next);
};
But since this module will increase adding more strategies I'm thinking if there is a way to put all of them inside a module.exports like:
module.exports = (passport) => {
function signin(req, res, next) {
passport.authenticate('local-login', function(err, user, token, info) {
if (err) {
return next(err); // will generate a 500 error
}
req.login(user, loginErr => {
if (loginErr) {
return next(loginErr);
}
return res.send({ success : true, message : 'signin succeeded' });
});
})(req, res, next);
};
I know that dosn't work Im just wondering if there is a possible solution like that so when I need to require those methods on my router file for example I can do this:
// auth.js (passport is passed from index.js)
const passportController = require('../controllers/passport-controller')(passport);
// Process the signup form
router.post('/signup', passportController.signup);
router.post('/signin', passportController.signin);
Instead of:
// auth.js (passport is passed from index.js)
const passportController = require('../controllers/passport-controller');
// Process the signup form
router.post('/signup', passportController.signup(passport));
router.post('/signin', passportController.signin(passport));
As you can see is just matter of looking for the most legible way to code and keep it simpler as possible.
Any help would be appreaciated, thank you very much.
Sounds like you want to make a module that exports a single function which returns an object. The syntax for that is
module.exports = passport => ({
signup(req, res, next) {
…
},
signin(req, res, next) {
…
}
});
I'm writing some rendering-code for an Express app, I wish to catch errors and then output them in the function render, but I'm not sure how I'm going to move them from one method to the other.
app.get('/user/makeRider', auth,
(req, res, next) => {
req.user.user.makeRider(req.query)
.catch(error)
.then(render(req, res));
}
);
var render = (req, res) => {
var response = {
params: req.query,
user: req.user.fulluser
};
res.json(response);
},
error = (reason) => {
reason.errors.forEach((error) =>{
console.log(error);
});
return;
};
You can use error function as your last midleware in the chain and simply pass the request to the next chain:
var render = (req, res) => {
var response = {
params: req.query,
user: req.user.fulluser
};
res.json(response);
}
app.get('/user/makeRider', auth,
(req, res, next) => {
req.user.user.makeRider(req.query)
.catch(next)
.then(render(req, res));
}
);
app.use((reason, req, res, next) => {
res.send(reason.errors);
// or you can send them as json: res.status(404).json({errors: reason.errors})
});
Beware of hoisting issue in your code, the variable declarations are hoisted to the top, but not their assignments, error and render function may appear as undefined when accessed from your route.
A quick, but maybe not the most elegant solution would be to add errors as a parameter to your render function, then you could do something like this:
app.get('/user/makeRider', auth,
(req, res, next) => {
req.user.user.makeRider(req.query)
.catch((reason)=>{
render(req, res, reason.errors)
})
.then(render(req, res));
}
);
var render = (req, res, errs) => {
var response = {
params: req.query,
user: req.user.fulluser
};
res.json(response);
};
isAuthenticated() {
return compose()
// Validate jwt
.use(function(req, res, next) {
// allow access_token to be passed through query parameter as well
if (req.query && req.query.hasOwnProperty('access_token')) {
req.headers.authorization = 'Bearer ' + req.query.access_token;
}
validateJwt(req, res, next);
})
// Attach user to request
.use(function(req, res, next) {
User.findById(req.user._id)
.exec()
.then(user => {
if (!user) {
return res.status(401).end();
}
req.user = user;
return next();
})
.catch(err => { console.log(err); next(err) });
});
}
I am writing a piece of composable middle ware and the .then for the User.findById throws the following error Warning: a promise was created in a handler but was not returned from it. why does this happen? I return the next(); and if the user is not found i also return.
I have an ExpressJS controller that list all my users
userCtrl.get :
get(req, res, next) {
var func = function(err, data) {
if (err) return next(err);
return res.json(data);
};
if (req.params[this.idName])
this._getById(req.params[this.idName], func);
else
this._getAll(func);
}
_getById(id, fn) {
this.ObjectClass.findById(id, fn);
}
_getAll(fn) {
this.ObjectClass.findAll(fn);
}
I'd like to call it from another road, in such a way that res.json() will filter a field of this json
Something like :
router.get ('/services/:serviceKey/authBridge/users', function(req, res, next) {
function anonJs(x) {
x.forEach(s => s.credential = null);
res.json(x);
}
res.json = anonJs;
userCtrl.get(req, res, next);
});
The problem is, with this last piece of code I end up with a recursion as I call res.json that is now defined as anonJS
You must store the reference to the old function before replacing it.
router.get ('/services/:serviceKey/authBridge/users', function(req, res, next) {
var json = res.json;
res.json = function(x) {
x.forEach(s => s.credential = null);
json(x);
}
userCtrl.get(req, res, next);
});
I have the following code :
server.use(function(req, res, next) {
users_db.set(req.user, function(err) { // async call to mongodb
if (err) {
console.error(err);
}
});
}
return next();
});
server.get('/', function(req, res) {
req.user.active = true; // this is a new field in user object
res.send(req.user);
}
});
So, As you see, when users_db.set() is called, req.user doesn't have the active=true field. It is being inserted only in the server.get() function.
Is it possible that user.active = true is registered in the db nevertheless because of the asynchronous nature of the call ?
As far as I know (it is like that in Express at least) .get method accepts many middleware functions. So I guess that the following will work:
server.get(
'/',
function(req, res, next) {
req.user.active = true; // this is a new field in user object
res.send(req.user);
next();
},
function(req, res, next) {
users_db.set(req.user, function(err) { // async call to mongodb
if (err) {
console.error(err);
}
});
}
return next();
}
);
Doing the things like that you are sure that req.user.active is populated always before to reach the moment with users_db.set.