zedjay72
zedjay72

Reputation: 177

invoking object methods with extra parenthesis

I'm looking at the passport.js documentation and I noticed this code:

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);

});

What's going on with the (req, res, next) on the second to last line there?

From what I understand, this isn't the right syntax or context for an IIFE, and req, res and next should already be available to the authenticate function.

Thanks,

Zakiir

Upvotes: 0

Views: 61

Answers (1)

danneu
danneu

Reputation: 9454

passport.authenticate() returns a middleware function.

i.e. it has the signature fn(req, res, next).

When you want to run it within a route, you have to invoke it yourself and pass it those arguments so that it can, for example, access data from the request, manipulate the response, etc.

app.get('/login', function(req, res, next) {
  passport.authenticate('local')(req, res, next);
});

That runs your local strategy and does whatever it is that it's configured to do.

Had you just written this:

app.get('/login', function(req, res, next) {
  passport.authenticate('local')();
});

Then req, res, and next are not available to the authenticate function.

Simply invoking a function does not give it access to variables that are in scope at the call site. For example, this doesn't work:

function addOne() {
  x++;
}

app.get('/', function() {
  var x = 99;
  addOne();
})

It's when you define a function that the function can access variables in scope at the definition site whenever/wherever you invoke it.

Your snippet passes in (defines) a callback that's run after the local strategy has run which lets you run arbitrary logic after the local strategy succeeded or failed to authenticate the user.

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);
});

This callback can access req, res, and next since they are in scope when the callback function is defined.

However, you still need to pass req, res, and next when invoking passport's authenticate function so that it can access them. That function is already defined in the library so, unlike the defined callback function, it did not capture the res, res, and next variables inside the route.

By the time the defined callback function is run, the req/res variables in scope have possibly been mutated by whatever is happening in the local strategy that authenticate runs. The callback function gets to read the state of the variables and altogether has the final word — it's essentially post-processing the result of the authenticate method, allowing you complete freedom to write your own logic if Passport's built-in options aren't enough.

The difference in scoping behavior between the invocation site vs the definition site of a function is what results in the confusing snippet.

Upvotes: 1

Related Questions