Is it possible to skip route middleware in node express? - node.js

Take the following POST function in express. (I am using express 3.5.1)
app.post('/example', someFunctionOne, someFunctionTwo, function(req, res){
if(!req.someVar){
return res.send(400, { message: 'error'});
} else{
return res.json(200, { message: 'ok'});
}
});
If I get some result from someFunctionOne which means someFunctionTwo is redundant, is there a way to skip someFunctionTwo and go to the last unnamed function which will send the response?
So I guess in the same way there is the "next()" function where is the "last()" function? If this is not possible why not? It seems like an oversight to me but is there a good reason?

You can do next('route') which will go to the next route entirely. This is not exactly what you need in this case, but it would work if you broke your code up into 2 separate routes.
However, I think there are generally 2 approaches to this kind of conditional logic:
make someFunctionOne put some state on the req instance when it gets the special result and make someFunctionTwo smart enough to check for that and when found call next() and bypass itself. This is the most idiomatic express thing to do, and it's how most middleware detect when they have been called more than once on the same request and avoid redoing their work again.
In someFunctionOne, when the special case happens, just invoke lastFunction directly. Remember the middleware abstraction isn't the holy grail. If your middleware are so tightly coupled, maybe they should be one middleware and some helper functions. There are lots of other ways to organize code that might feel more natural.

My instinct is to do something like this:
const funcOne = (req, res, next) => {
// do something
if (/* you were successful */) {
res.locals.shouldSkipFuncTwo = true
}
next()
}
const funcTwo = (req, res, next) => {
if (res.locals.shouldSkipFuncTwo) return next()
// do whatever stuff
next()
}
router.get('/', funcOne, funcTwo, (req, res) => {
res.status(200).send('hello world')
)}
If you haven't used res.locals before, here are the express docs on it. Basically, it's a property in the response object that's there for you to use as a container.

Probably the best way to do it to make some helper or put your own middleware into chain instead of your functions.
So your code will look like:
app.post('/example', oneOf(key, someFunctionOne, someFunctionTwo), function(req, res){
if(!req[key]){
return res.send(400, { message: 'error'});
} else{
return res.json(200, { message: 'ok'});
}
});
And the helper should be something like that:
function oneOf (key) {
var fns = Array.prototype.slice.call(arguments, 1);
var l = fns.length;
return function (req, res, next) {
var i = 0;
function _next () {
if (req[key] || i === l) return next();
fns[i](req, res, _next);
i += 1;
}
_next();
}
}
If you will decide to do it just here the code will looks like:
app.post('/example', functionOneOrTwo, function(req, res){
if(!req.someVar){
return res.send(400, { message: 'error'});
} else{
return res.json(200, { message: 'ok'});
}
});
function functionOneOrTwo(req, res, next) {
someFunctionOne(req, res, function () {
if (req.someVar) return next();
someFunctionTwo(req, res, next);
});
}
Simple, but untested ;-)

Actually I was facing the very same problem. And I just found the express-unless module which does exactly this: https://github.com/jfromaniello/express-unless

Related

Return res and next in a custom express function

How do I return both res and next in an express function:
const customfunction = async (req, res, next) => {
try {
// how do I set cookie and return next()?
return res.cookie('someToken', someToken, {
signed: true,
// etc...
}
);
return next();
} catch (err) {
// catch here, for example, return res.status(401).clearCookie...
}
}
An express request handler (like something you pass to app.get() or router.post() or something like that) does not pay any attention to the return value from that handler.
So, return inside such a handler is used only for flow control to stop further execution of the function.
In addition, your code has two return statements one after the other:
return res.cookie(...);
return next();
Which makes no sense because the return next() line of code will never be executed as the function has already returned on the line before.
If this is middleware and you intend for some other request handler to still have a chance to process this request, then you would want something like this:
const customfunction = async (req, res, next) => {
res.cookie('someToken', someToken);
next();
};
It doesn't appear that there's any reason for the try/catch because neither of these should throw an exception (assuming your code doesn't have a syntax error in it).
But, if you really wanted the try/catch, you could do this:
const customfunction = async (req, res, next) => {
try {
res.cookie('someToken', someToken);
next();
} catch(e) {
// make sure logs can see that this unexpected error is happening
console.log("customFunction error", e);
res.clearCookie('someToken');
res.status(500).send("Internal Error"); // probably want a more glamorous error page
}
};

Express.js - Get the res.send value

My question is exactly this one. The problem is that when I apply the solution to that question, nothing happens - the value of res.send isn't logged. I use express 4.16.4.
Here's my code:
/* FILE: /app.js: */
...
const postRoute = require('./middleware/postRoute');
const myRoute = require('./routes/myRoute');
...
app.use('/', myRoute);
app.use(postRoute);
...
/* FILE: /routes/myRoute */
...
router.post('/myRoute', [], (req, res, next) => {
res.send({ status:'success', message:'Test success. Feels good, man.' });
next();
});
...
/* FILE: /middleware/postRoute */
const postRoute = function(req, res, next) {
console.log('postRoute: ');
var send = res.send;
res.send = function(chunk, encoding){
res.send = send;
if (chunk) {
console.log('postRoute chunk: ', chunk); // This statement is never reached
}
res.send(chunk, encoding);
};
next();
};
module.exports = { postRoute };
When I make a POST request to /myRoute using Postman, it logs the following: postRoute:, and that's it. The second console.log() is never reached and presumably neither is any other statement in the same function() as the console.log().
The question I link to at the start of my question was from 2015, when I assume they used a different version of Express.js, and that's why their solution isn't working for me? In my middleware code example, I used the code from a question that was linked in one of that question's answers, because it's solution didn't work for me. But that solution's obviously also not working, otherwise I wouldn't be here - and it's from 2012! Who even knows what version of Express they used back then!?
So to reiterate and conclude my question: How do I use middleware to log the value passed to res.send()?
Follow up question (but let me know if I should rather ask this in a separate question): Is there a way to call middleware after the route/response from the route instead of globally. So router.post('/myRoute', [postRoute], (req, res, next) => {...}); instead of app.use(postRoute)?
Here's what I ended up doing. I kept my file structure exactly the same, but instead of res.send()ing from the route, I attached the object that I would have sent, to the res object and then call next() at the end of my route. So for instance, res.send({message:'blah'}); becomes res.return = {message:'blah'}; next();. I use .return because I don't believe such a property exists on the res object by default and I find it descriptive enough for my purposes. Then in my postRoute middleware, I can easily access res.return, after which I call return res.send(res.return).
Using my example from the question (app.js stays exactly as you see it):
/* FILE: /routes/myRoute */
...
router.post('/myRoute', [], (req, res, next) => {
if(theConditionIsMet()) {
res.return = { message:'The condition was met.' };
} else {
res.return = { message:'The condition was not met.' };
}
next();
});
....
router.post('/anotherRoute', [], (req, res, next) => {
// In this route I don't need to intercept the response, so I just do it like normal.
return res.send({ message:'Feels good, man.' });
});
...
/* FILE: /middleware/postRoute */
const postRoute = function(req, res, next) {
if(res.hasOwnProperty('return') {
return res.send(res.return);
}
};
module.exports = { postRoute };
One flaw I can already see is that I'll now have to restructure all my routes to use this method of res.send()ing (or at least all of the routes for which I want to intercept the return value). Because of this, and because I imagine someone more knowledgeable than me could probably figure out a better way to do it, I'm not going to accept my own answer.

Skipping a middleware in express

I'm not looking for authorization of a particular request and so I did this.If the request path matches I want to skip the auth.verifyToken middleware.How can I do this.I tried using return next() but its not working.
eventRouter.param('pin', (req, res, next, pin) => {
let path = `/event/matchpin/${pin}`;
if(req.path === path){
//do something here so that directly executes the route
}
//else it executes the auth.verifyToken middleware
next();
});
app.use('/user',auth.verifyToken,eventRouter);
next() is used to skip the middleware, you are just using it at the wrong place.
Try this code:
eventRouter.param('pin', (req, res, next, pin) => {
let path = `/event/matchpin/${pin}`;
if(req.path === path){
// Next will by pass this middleware
next();
}
//else it executes the auth.verifyToken middleware
});
app.use('/user',auth.verifyToken,eventRouter);

How do I handle errors in a function called from a route handler in Express, NodeJS?

This may be extremely stupid, but I haven't found much about this because I don't understand how I should search this.
I have a route handler that may call different functions depending on some request parameters, and I would like to know what's the best way to deal with errors inside the functions in order to pass errors to the error handling middleware.
Consider something like this:
router.get('/error/:error_id', (req, res, next) => {
my_function();
}
function my_function(){
// do something async, like readfile
var f = fs.readFile("blablabla", function (err, data) {
// would want to deal with the error
});
}
If an error occurs during fs.readFile, how do I pass the error to next to forward it to the error middleware? The only solution is to pass the next param to the function function my_function(next){...}?
In case the function didn't call any async I/O operation, a simple try/catch in the route handler would be ok (i suppose), like this:
router.get('/error/:error_id', (req, res, next) => {
try{
my_function();
} catch(e){
next(e);
};
}
function my_function(){
// do stuff
var f = fs.readFileSync("blablabla"); // possibly throws an error
}
Hope I make some sense.
You are totally correct that you should pass the next callback to my_function since fs.readFile is asynchronous.
router.get('/error/:error_id', (req, res, next) => {
my_function(next);
}
function my_function(next) {
fs.readFile("blablabla", function (err, data) {
if (err) {
next(err);
} else {
// Process the data
// Don't forget to call `next` to send respond the client
}
});
}
By the way, you cannot do
var f = fs.readFile(...)
because fs.readFile is asynchronous. The data should be handled within the callback.

next() promise error handling

What would be the optimal way for error handling?
I need custom json error messages. It's an API.
exports.putCurso = function (req, res, next) {
util.updateDocument(req.curso, Curso, req.body);
req.curso.saveAsync()
.then(function (data) {
return res.status(201).json({message: 'Curso atualizado.', data: data});
})
.catch(function(error) {
return res.status(500).json({message: 'ERROR!'});
//OR return next(error); but I need custom json error messages so it doesn't make sense
})
.finally(next); //OR return next(error)? redundant?
};
I am no mongoose guy but I know one or two things about express and promise
exports.putCurso = function (req, res, next) {
util.updateDocument(req.curso, Curso, req.body);
req.curso.saveAsync()
.then(function (data) {
res.status(201).json({message: 'Curso atualizado.', data: data});
}, function(error){
res.status(500).json({message: 'ERROR!'});
})
};
And this is basically all that you need. Based on the implementation, this is probably a normal route because it always returns something (res.json) to the client. Therefore, you don't have to call next because it is meant for middlewares to call
Also you don't have to return anything because when you call res.json, it basically says that this request ends here, nothing else.
Last but not least, by specification, promise then supports 2 functions, the first one is for handing successful case, the 2nd one is for exceptions. So, you don't have to call catch
Considering Curso a mongoose document
You can do it like this
req.curso.save(function(err,data){
if(err) res.status(500).json({message: 'ERROR!'});
else res.status(201).json({message: 'Curso atualizado.', data: data})
});
EDIT : if you have so many similar issues through out your little huge node application, its worth looking at rb, then you can do it like
var RB = require('rb');
exports.putCurso = function (req, res, next) {
util.updateDocument(req.curso, Curso, req.body);
// the below line could have been written in some middleware (eg middleware provided by express.io), so we do get clear code in controller part.
res.RB = RB.build(res, { // you may customize your builder yours way, after looking into `rb` docs
errorStatus : 500, successStatus : 201,
errorKey : false, successKey : 'data',
preProcessError : function(){ return { message : 'ERROR!' } },
addToSuccess : { message : 'Curso atualizado.' }
});
//Now only one line in controller
req.curso.save(res.RB.all);
};
Disclosure : i am author of rb.
asCallback takes a callback which it calls with the promise outcome mapped to the callback convention:
If the promise is rejected, it calls the callback with the error as first argument: cb(error)
If the promise is fulfilled, it calls the callback with the value as the second argument: cb(null, value).
exports.putCurso = function (req, res, next) {
util.updateDocument(req.curso, Curso, req.body);
req.curso.saveAsync()
.then(function (data) {
return res.status(201).json({message: 'Curso atualizado.', data: data});
})
.catch(function(error) {
return res.status(500).json({message: 'ERROR!'});
//OR return next(error); but I need custom json error messages so it doesn't make sense
})
.asCallback(next);
};

Resources