66

I have a fairly simple Express.js app with a login component that I'd like to exit early if login fails. I'm seeing indications that the app isn't doing that and I haven't found a definitive answer that indicates whether calling res.send() halts any further processing. Here's my code as it stands now:

client.login( username, password, function( auth, client ) {
  if( !auth ) {
    res.send( 401 );
  }

  // DO OTHER STUFF IF AUTH IS SUCCESSFUL
}

If I read the source code correctly, it should end the request (aborting further processing), but I'm new to node, so I'm not quite ready to trust what I think I'm reading. To boil it down, I guess I'm mostly looking for a definitive answer from a more trustworthy source that my own interpretation of unfamiliar source code. If send() doesn't abort processing, what's the right way to do that?

Rob Wilkerson
  • 40,476
  • 42
  • 137
  • 192

8 Answers8

76

Of course express can not magically make your javascript function stop executing from somewhere else.

I don't like the next([error]) solution because I think errors should be only used for circumstances you usually don't expect (like an unreachable database or something). In this case, a simple wrong password would cause an error. It is a common convention to not use exceptions/errors for ordinary control flow.

I therefore recommend to place a return statement after the res.send call to make your function stop executing further.

client.login( username, password, function( auth, client ) {
  if( !auth ) {
    res.send( 401 );
    return;
  }

  // DO OTHER STUFF REALLY ONLY IF AUTH IS SUCCESSFUL
}
Steve Beer
  • 1,318
  • 12
  • 16
  • 12
    How about `return res.send(401);`? – Filippo Vigani Jul 02 '16 at 14:10
  • will work as well. I think that's a matter of taste. – Steve Beer Jul 04 '16 at 07:49
  • 21
    It's not just a matter of taste, it's semantically incorrect. Reading this code you would assume that `res.send()` returns something, but it doesn't return anything. You could continue to assume that `client.login()` also returns whatever `res.send()` returns, which is an incorrect assumption as well. It does not matter in your example, but this will create a habit that will manifest itself in places where it *does* matter. – Brian de Heus Dec 09 '16 at 01:51
  • 1
    As I commented in @Floby's answer (above), your `return;` solution is the only thing that actually worked as needed! I don't know if I will get consequences down the line, but for now I'll keep working with this solution... thanks! – Jeach Feb 12 '18 at 20:54
46

If you are using express as your framework, you should call next() instead.

Each handler in express receives 3 parameters (unlinke 2 for basic http) which are req, res and next

next is a function that when called with no arguments will trigger the next handler in the middleware chain.

If next is called with an arguments, this argument will be interpreter as an error, regardless of the type of that argument.

Its signature is next([error]). When next is called with an error, then instead of calling the next handler in the middleware chain, it calls the error handler. You should handle the 401 response code in that error handler. See this for more info on error handling in Express

EDIT: As @Baptiste Costa commented, simply calling next() will not cease the current execution but it will call on the next middleware. It is good practice to use return next() instead to prevent Node from throwing errors further on (such as the can't set headers after they are sent - error). This includes the above suggestion of error throwing which is common:

return next(new Error([error]));
Fatih Aktaş
  • 1,446
  • 13
  • 25
Floby
  • 2,306
  • 17
  • 15
  • 10
    Calling the next() function doesn't abort processing, therefore, the `// DO OTHER STUFF IF AUTH IS SUCCESSFUL` will be executed... – Baptiste Costa Apr 13 '14 at 13:19
  • For me it's not working at all. I have three routes; (a) for logging, (b) for authentication and lastly, (c) the last which only logs for debug purposes. In the second route, when no SessionID exists on the header it's suppose to fail/stop when I call `res.status(401).json({ ... });` followed by `next('error');` ... but it ALWAYS executes the third route regardless. My page gets the HTTP 401 but the third route gets executed and so does my app API routes. Very frustrating! **Update**: @Steve Beer's suggestion to `return;` instead of `next('error');` seems to work for me! – Jeach Feb 12 '18 at 20:48
5

For your specific case you can just add the 'else' statement:

client.login( username, password, function( auth, client ) {
    if( !auth ) {
        res.send( 401 );
    }else {
       // DO OTHER STUFF IF AUTH IS SUCCESSFUL
    }
}

Or, in general, you can use 'return':

return res.send( 401 );
RoccoDen91
  • 125
  • 3
  • 9
2

in these cases , i tend to use a try...catch bloc .

client.login( username, password, function( auth, client ) { 

 try{
   if(error1){
    throw {status : 401 , message : 'error1'}
   }
   if(error2){
    throw {status : 500 , message : 'error2'}
   }
 }catch(error){
   res.status(error.status).json(error.message);
 }

}
0

Simply do something like this to stop further execution.

function (request, response, next) {
    var path = request.body.path;

    if(path === undefined){
        response.status(HttpStatus.BAD_REQUEST);
        response.send('path is required!');
    }

    next(response)
};
Varun
  • 49
  • 1
  • 11
0

You can call the next function like this:

const express = require('express');
const app = express();

app.get('/',(req,res,next) => {
  res.sendStatus(200) //response
  res.end() // to prevent request hanging
  next() //do other stuff if request is done
})
app.listen(80)
Tyler2P
  • 2,324
  • 26
  • 22
  • 31
-2

You only need to do a return to end the flow:

return  res.send( 401 );

That will send the 401 response back and won't proceed forward in the flow.

alexalejandroem
  • 1,094
  • 12
  • 17
-8

Why is no-one suggesting using an 'else' block?

if(!auth){
    // Auth fail code
    res.send 'Fail'
} else {
    // Auth pass code
    res.send 'Pass'
}

'next' is used when creating your own middleware for the "app.use(function(req, res, next));". If you have set up a route like "app.get(route, function(req, res));" then the code in the function is where you can have the code you are specifying without needing to use 'next'.

frank
  • 153
  • 1
  • 2