Sabbin
Sabbin

Reputation: 2445

Passing parameters from one module to another in Node.js

I'm working on a REST API in node.js and I'm encountering a problem when trying to pass a value from one module to another, I'm rather new to node.js there is something I'm definitely missing something here...

I have a controller and a "service" module for authentication which uses JWT. What I'm trying to do is pass a user object to the auth service in order to obtain the JWT and pass it back to the controller in order to build the response.

In the service I get the JWT with no problems but I cannot pass it back... the return param; does not work in this case.

Here is the function from my controller

exports.login = (req, res) => {
    const email = req.body.email;
    const password = req.body.password;

    Users.findOne({ 'email': email, 'password': password }, (err, user) => {
        if (err) {
            res.json({ err });
        } else {
            if (typeof user !== null) {                
                var token = authService.generateToken(user);
                res.json({
                    token
                });

            } else {
                res.json({
                    status: 'error',
                    message: 'User not found!'
                });
            }

        }
    })
};

And here is the function from my authService (which is inclunded in the controller)

exports.generateToken = function(user) {
    jwt.sign({ user }, params.secret, { expiresIn: params.expires }, (err, token) => {
        if (err)
            return err;
        console.log(token);
        return token;
    });
}

Now that console.log(token) from the authService returns the valid JWT in the console, but in the controller I get nothing back.

Can someone please help me with this?

Thanks!

Upvotes: 0

Views: 171

Answers (1)

Patrick Roberts
Patrick Roberts

Reputation: 51816

You can't return something from an asynchronous callback. You have to provide a callback function to pass the value to:

exports.login = (req, res) => {
  const email = req.body.email;
  const password = req.body.password;

  Users.findOne({ email, password }, (err, user) => {
    if (err) {
      res.json({ err });
    } else {
      if (typeof user !== null) {                
        authService.generateToken(user, req, (err, token) => {
          res.json({
            token
          });
        });
      } else {
        res.json({
          status: 'error',
          message: 'User not found!'
        });
      }
    }
  });
};

exports.generateToken = function(user, req, callback) {
  jwt.sign({ user }, req.params.secret, { expiresIn: req.params.expires }, callback);
}

Alternatively, you can convert your callbacks to promises to somewhat flatten your asynchronous flow:

exports.login = (req, res) => {
  const email = req.body.email;
  const password = req.body.password;

  Users.findOne({ email, password }).then(user => {
    if (user === null) throw new Error('User not found');
    return authService.generateToken(user, req);
  }).then(token => {
    res.json({ token });
  }).catch(error => {
    res.json({ status: 'error', message: error.message });
  });
};

exports.generateToken = (user, req) => new Promise((resolve, reject) => {
  jwt.sign({ user }, req.params.secret, { expiresIn: req.params.expires }, (error, token) => {
    if (error) reject(error);
    else resolve(token);
  });
});

And finally, leaving exports.generateToken() as written above, you can convert your promise-based exports.login() to an async function:

exports.login = async (req, res) => {
  const email = req.body.email;
  const password = req.body.password;

  try {
    const user = await Users.findOne({ email, password });

    if (user === null) throw new Error('User not found');

    const token = await authService.generateToken(user, req);

    res.json({ token });
  } catch (error) {
    res.json({ status: 'error', message: error.message });
  }
};

Upvotes: 1

Related Questions