Reputation: 2445
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
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