0

I'm writing an API with NodeJS and Express for a schoolproject and I'm struggling with the following:

The function getAuthUserId decodes the JWT token and gets the Id from user in the mongoDB server.

I call this function in a REST call "/user/authTest". But when I call this, the server responds before the database can return the Id, and the variable UId is undefined. As you can see, the Id is actually found. Any ideas on how i can fix this?

The API call code:

apiRoutes.post('/user/authTestID', function(req, res) {
  var UId = getAuthUserId(req, res);
  console.log(UId);
  if (UId) {
    res.sendStatus(200);
  }else{
    res.sendStatus(400);
  }

}); 

The function:

function getAuthUserId(req, res) {
    var user = new User();
  var token = user.getToken(req.headers);
  if (token) {
    var decoded = jwt.decode(token, config.secret);
    User.findOne({
      name: decoded.name
    }, function(err, user) {
        if (err) throw err;

        if (!user) {
          res.status(403).send({success: false, msg: 'Authentication failed. User not found.'});
          return false
        } else {
          console.log('Auth for ' + user.name + ' ' + user._id);
          return user._id
        }
    });
  } else {
    res.status(403).send({success: false, msg: 'No token provided.'});
    return '';
  }
}

The output of the terminal:

[nodemon] restarting due to changes...
[nodemon] starting `node server.js`
Connected to MongoDB
undefined
::ffff:192.168.0.111 - POST /user/authTestID HTTP/1.1 400 11 - 175.006 ms
Auth for test 58f8954c3602b80552b6f1fb

Thanks in advance!

Krapton
  • 45
  • 1
  • 10
  • 2
    Possible duplicate of [How do I return the response from an asynchronous call?](http://stackoverflow.com/questions/14220321/how-do-i-return-the-response-from-an-asynchronous-call) – James Thorpe May 02 '17 at 14:59
  • True! I thought it was something completely different because I didn't know much about AJAX before, and thought it didn't have anything to do with JS. I will look into it now! – Krapton May 02 '17 at 15:12

3 Answers3

2

You need to make it a promise, like this.

API

apiRoutes.post('/user/authTestID', function(req, res) {
  getAuthUserId(req, res).then(function (UId) => {
    console.log(UId);

    if (UId) {
      res.sendStatus(200);
    }else{
      res.sendStatus(400);
    }
  });

}, function(err) {
    console.log(err.msg)
    res.status(err.status).send(err.msg);
});

Function

function getAuthUserId(req, res) {
    return new Promise(function(resolve, reject){
        var user = new User();
        var token = user.getToken(req.headers);
        if (token) {
            var decoded = jwt.decode(token, config.secret);
            User.findOne({
                name: decoded.name
            }, function(err, user) {
                if (err) throw err;

                if (!user) {
                    reject({status: 403, msg: 'Authentication failed. User not found.'});
                } else {
                    console.log('Auth for ' + user.name + ' ' + user._id);
                    resolve(user._id)
                }
            });
        } else {
            reject({status: 403, msg: 'No token provided.'});
        }
    })
}
Joe Lissner
  • 2,181
  • 1
  • 15
  • 21
2

getAuthUserId get's the value in a CALLBACK !!! . You can't expect it to return values from it. As quick thing you can do something as below.

        apiRoutes.post('/user/authTestID', function (req, res) {
            var user = new User();
            var token = user.getToken(req.headers);
            if (token) {
                var decoded = jwt.decode(token, config.secret);
                User.findOne({
                    name: decoded.name
                }, function (err, user) {
                    if (err) throw err;
                    if (!user) {
                        return res.status(403).send({success: false, msg: 'Authentication failed. User not found.'});
                    } else {
                        console.log('Auth for ' + user.name + ' ' + user._id);
                        return res.send(user._id)
                    }
                });
            } else {
                return res.status(403).send({success: false, msg: 'No token provided.'});
                // return '';
            }
        });

Try using Promise library like Bluebird

Anuranjit Maindola
  • 1,507
  • 1
  • 14
  • 30
  • Thank you for the suggestion! I already read about promises, but I didn't know this was the kind of situation to use them in! – Krapton May 02 '17 at 15:10
1

James' comment looks like a good, thorough resource on async calls. As others have mentioned, you cannot return values within a callback. You can use a Promise library, or you can change your getAuthUserId function to take a callback and have your console.log logic in there:

Example:

API call code:

apiRoutes.post('/user/authTestID', function(req, res) {
  getAuthUserId(req, res, function(UId) {
  // we're in a your new callback
  console.log(UId);
  if (UId) {
    res.sendStatus(200);
  }else{
    res.sendStatus(400);
  }
  });
});

Function Code

// note new callback param
function getAuthUserId(req, res, callback) {
  var user = new User();
  var token = user.getToken(req.headers);
  if (token) {
    var decoded = jwt.decode(token, config.secret);
    User.findOne({
      name: decoded.name
    }, function(err, user) {
        if (err) throw err;

        if (!user) {
          res.status(403).send({success: false, msg: 'Authentication failed. User not found.'});
          callback(false) // no more return, call callback with value
        } else {
          console.log('Auth for ' + user.name + ' ' + user._id);
          callback(user._id) // no more return, call callback with value
        }
    });
  } else {
    res.status(403).send({success: false, msg: 'No token provided.'});
    callback(''); // no more return, call callback with value
  }
}
dev.kho
  • 219
  • 2
  • 6