I think that I know why we use next() in expressjs in theory: :)
In fact, the routing methods can have more than one callback function as arguments. With multiple callback functions, it is important to provide next as an argument to the callback function and then call next() within the body of the function to hand off control to the next callback.
But in practice I can't get it.
app.get('/project', (req, res, next) => {
con.query("SELECT * FROM issues", (err, result) => {
if (err) throw err;
res.send(result);
})
next()
console.log("NEVER HERE");
});
This is situation where this code do not work.
Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
But if I put next() right bellow res.send(result); and remove this one above console.log('NEVER HERE'); statement it works perfectly.
Also if I remove next() on both places it works well.
And what I noticed is if I use console.log(result) instead of res.send(result) it works no metter where next() callback is.
My main question is why one next() "makes troubles" to other one, when they are used together with res.send().
I'm new to express and also with mysql, thank you in advance. :)
You are not supposed to use both res.send and next
The purpose of next is to pass control to another middleware but when you respond to the request using res.send(...) there's no reason to that.
Simple example when you would use next:
app.get('/admin', function authMiddleware(req, res, next){
// user must be logged in and be an admin to access this route
if (req.user && req.user.isAdmin)
next(); // this will pass control to the handler function bellow
else // respond with 401 error
res.status(401).send('unauthorized');
}, function handler(req, res){
// only get here when user is admin
res.render('admin');
});
Your example should look like this, you don't need to care about next at all:
app.get('/project', (req, res) => {
con.query("SELECT * FROM issues", (err, result) => {
// don't throw, just respond appropriately
//if (err) throw err;
if (err)
res.status(500).send('error occured');
else
res.send(result);
});
// calling `next` here doesn't make any sense unless you pass an error
});
You could use the next function but you should have setup an error handler and you need to pass the error (Docs):
app.get('/project', (req, res, next) => {
con.query("SELECT * FROM issues", (err, result) => {
// don't throw, just respond appropriately
//if (err) throw err;
if (err)
next('error occured');
else
res.send(result);
});
});
// Error handler
app.use(function (err, req, res, next) {
console.error(err)
res.status(500).send('error occured')
});
Related
I am trying to authenticate a user with custom callback in passport js. I have written my code based on the passport documentation.
router.post("/signin/email", function (req, res, next) {
passport.authenticate("email-local", function (err, user, info) {
if (err) {
return res.send("err");
}
if (!user) {
return res.send(info);
}
req.logIn(user, function (err) {
if (err) {
return res.send(err);
}
next(user);
});
})(req, res, next),
UsersController.getToken;
});
I want to pass the user to the next middleware i.e UsersController.getToken ,but it is not being passed. How can i solve this issue?
Passport will add the user data to req.user after login is called so you don't need to pass it with next.
I would recommend simplifying the req.logIn call to the following:
return req.logIn(user, next)
Then within you UsersController.getToken middleware you can access the user data through the req object.
I do not want a redirect in my sign-up route, it is a two stage process (the way I currently have it figured out), so I would like to submit the first form, save to db with ajax and return something, and then show the second form to complete sign up. The post route works, but the function does not run
router.route('/register')
.post((req, res, next) => {
console.log('this bit here works');
passport.authenticate('local-signup', function(error, user) {
console.log('it's here that nothing happens');
if(error) {
return res.status(500).json(error);
}
return res.json(user); //this is what I want to return;
})
})
Does passport only work with the one post call?
passport.authenticate() is an Express middleware, not a regular function.
As per the fine manual (search for "Custom Callback"), it should be used like this:
router.route('/register').post((req, res, next) => {
console.log('this bit here works');
passport.authenticate('local', function(error, user, info) {
console.log("it's here that something should happen now.");
if (error) {
return res.status(500).json(error);
}
return res.json(user);
})(req, res, next);
})
FWIW, user may not necessarily be a proper object (if authentication failed, for instance).
I have a sequelize database that validates data and throws errors.
I know I can do something like this to catch and output my errors:
User.build()
.catch(Sequelize.ValidationError, function (err) {
// respond with validation errors
return res.status(422).send(err.errors);
})
.catch(function (err) {
// every other error
return res.status(400).send({
message: err.message
});
But I don't want to add it to every single request, is there some generic way to catch theese errors?
You can add a custom method to req (or res) that will resolve the promise and handle any errors:
app.use((req, res, next) => {
req.resolve = (promise) => {
return promise.catch(Sequelize.ValidationError, err => {
// respond with validation errors
return res.status(422).send(err.errors);
}).catch(err => {
// every other error
return res.status(400).send({ message: err.message });
});
});
next();
});
Usage (provided that the middleware above is added before your routes):
router.post('/user', (req, res) => {
req.resolve(User.build()).then(user => res.json(user));
});
ES.next version (2016):
you can use async functions that throw using this wrapper function copied from the official strongloop website:
let wrap = fn => (...args) => fn(...args).catch(args[2]);
then make the function in your router/controller like that:
router.post('/fn/email', wrap(async function(req, res) { ...throw new Error(); }
and finally have a normal catch all errors middleware:
app.use(function(err, req, res, next) { console.log(err); }
Obviously for this to work you need the babel transpiler currently
I would like some help understanding the following example from the passport.js authenticate documentation:
app.get('/login', function(req, res, next) {
passport.authenticate('local', function(err, user, info) {
if (err) { return next(err); }
if (!user) { return res.redirect('/login'); }
req.logIn(user, function(err) {
if (err) { return next(err); }
return res.redirect('/users/' + user.username);
});
})(req, res, next); //***UNSURE ABOUT THIS***
});
I understand what the code does - but I don't know what the (req, res, next)at the end of the callback function is for. Why is it necessary? Does it provide the values for (err, user, info)? If that's the case, why don't I see more function calls ending with arguments - Is it perhaps something to do with passing on the next object?
Would love for someone to help me improve my understanding of this concept.
Request handlers are Express middleware; they get a request, a response, and a way to pass on execution to the next layer of middleware. passport.authenticate returns middleware, but it hasn’t been attached with app.use(), so you have to pass the appropriate arguments manually.
The fact that the callback from passport.authenticate also has three arguments is just a coincidence. They won’t have the same values.
What's the proper way to use custom callbacks with express.js functions?
Here's an example:
//routes/reset.js
user.save(function(err){
if ( err ) return next(err);
reset.send_reset_email(req, res, user, function(req, res){
req.flash('info', 'Check your email for a link to reset your password.');
res.redirect('/');
});
});
What signature should I use for reset.send_reset_email for this to work correctly?
This is what I have:
exports.send_reset_email = function(req, res, user, next){
//send email
transport.sendMail(options, function(err, responseStatus) {
if (err) {
console.log(err);
} else {
next(req, res);
//do I need to explicitly pass req, res here?
//is next() reserved word here?
}
});
});
Do I need to explicitly pass req, res here?
is next() reserved word here?
next() accepts an error or another route and is usualy called to continue with the next middleware.
in your send_reset_email function your next() isn't express's next() because you pass in function(req,res) and not next, pass in your own callback instead to handle the outcome of your sendmail function.
user.save(function(err){
if (err) return next(err) // if thats express's next()
reset.send_reset_email(req, res, user, function(err, data){
if(err) {
// send err msg
} else {
req.flash('info', 'Check your email for a link to reset your password.');
res.redirect('/');
}
});
});
xports.send_reset_email = function(req, res, user, cb){
//send email
transport.sendMail(options, function(err, responseStatus) {
if (err) return cb(err)
cb(null,responseStatus)
})
})