Passing callback as function argument - javascript

I have a hard time with callback and couldn't figure out if this is actually the right way to pass a callback function as an argument. Please help me out. Below is my code snippet
Service module
jwt.signToken({userName: success.data.username, userId: success.data.userId}, secret, expiresIn, (error, token) => {
if(error) {
console.log('::::::::::Error generating token::::::::');
console.log(error);
next(returnError({message: 'Internal Server Error', status: 500}));
} else {
sendSuccessResponse({data: {token: token, user: {userName: success.data.username, userId: success.data.userId},status: 200}}, response);
}
});
Auth module
const signToken = (payload, secret, expireIn, callback) => {
jwt.sign(payload, secret, {expiresIn: expireIn}, (error, token) => callback(error, token));
};
Thanks in advance

I'm able to successfully execute the code..Reiterating look at the code how I'm handling the callback in service layer
jwt.signToken({userName: success.data.username, userId: success.data.userId}, secret, expiresIn, function(error, token) {
if(error) {
next(returnError({message: 'Internal Server Error', status: 500}));
} else {
sendSuccessResponse({
data: {
token: `${token}`,
user: {
userName: success.data.username,
userId: success.data.userId
}
},
status: 200
},response);
}
});
And my jwt module is
const signToken = (payload, secret, expireIn, callback) => {
jwt.sign(payload, secret, {expiresIn: expireIn}, (error, token) => callback(error, token));
};
This can be a simple demonstration of passing callback implementation (done in one module) to another function (in another module) as a parameter.

Related

Node JS throwing cannot set headers after they are sent to the client, after using mongoose.removeOne

I have a method that deletes products and before it does it check if the user who is trying to delete the product is the user who created it. When i execute it with Insomnia it successfully removes the product but i get an error on the console saying cannot set headers after they are sent to the client.
My method:
exports.deleteProduct = (req, res) => {
const id = req.params.productId;
Product.deleteOne({ _id: id, userId: req.user._id }, () => {
return res.status(401).json("Not authorized");
})
.then(() => {
return res.status(200).json("Product deleted");
})
.catch((err) => {
return res.status(500).json({
error: err,
});
});
};
I'm pretty sure this is happening because I'm chaining a .then() and .catch() after executing it.
I tried to do this but it didn't work because the err parameter that I'm sending to the callback function is null.:
exports.deleteProduct = (req, res) => {
const id = req.params.productId;
Product.deleteOne({ _id: id, userId: req.user._id }, (err) => {
if (err) {
return res.status(401).json("Not authorized");
}
return res.status(200).json("Product deleted");
});
};
When i tried this second approach I always got the 200 status, meanwhile the product didn't delete.
Any idea how to deal with this?
You can try something like this:
Product.deleteOne({ _id: id, userId: req.user._id }, (err, result) => {
if(err) {
return "something"
}
return "something else"
});
or: in async / await way
try {
await Product.deleteOne({ _id: id, userId: req.user._id });
} catch (err) {
// handle error here
}
By the way, why you are passing userId at the deleteOne method?

Handling errors in Express.js in service / controller layers

I am writing an application in Express.js with a separate controller layer and a service layer. Here is my current code:
user.service.js
exports.registerUser = async function (email, password) {
const hash = await bcrypt.hash(password, 10);
const countUser = await User.countDocuments({email: email});
if(countUser > 0) {
throw ({ status: 409, code: 'USER_ALREADY_EXISTS', message: 'This e-mail address is already taken.' });
}
const user = new User({
email: email,
password: hash
});
return await user.save();
};
exports.loginUser = async function (email, password) {
const user = await User.findOne({ email: email });
const countUser = await User.countDocuments({email: email});
if(countUser === 0) {
throw ({ status: 404, code: 'USER_NOT_EXISTS', message: 'E-mail address does not exist.' });
}
const validPassword = await bcrypt.compare(password, user.password);
if (validPassword) {
const token = jwt.sign({ email: user.email, userId: user._id }, process.env.JWT_KEY, { expiresIn: "10s" });
return {
token: token,
expiresIn: 3600,
userId: user._id
}
} else {
throw ({ status: 401, code: 'LOGIN_INVALID', message: 'Invalid authentication credentials.' });
}
};
user.controller.js
exports.userRegister = async function (req, res, next) {
try {
const user = await UserService.registerUser(req.body.email, req.body.password);
res.status(201).json({ data: user });
} catch (e) {
if(!e.status) {
res.status(500).json( { error: { code: 'UNKNOWN_ERROR', message: 'An unknown error occurred.' } });
} else {
res.status(e.status).json( { error: { code: e.code, message: e.message } });
}
}
}
exports.userLogin = async function (req, res, next) {
try {
const user = await UserService.loginUser(req.body.email, req.body.password);
res.status(200).json({ data: user });
} catch (e) {
if(!e.status) {
res.status(500).json( { error: { code: 'UNKNOWN_ERROR', message: 'An unknown error occurred.' } });
} else {
res.status(e.status).json( { error: { code: e.code, message: e.message } });
}
}
}
The code works, but requires some corrections. I have a problem with error handling. I want to handle only some errors. If another error has occurred, the 500 Internal Server Error will be returned.
1) Can I use "throw" object from the service layer? Is this a good practice?
2) How to avoid duplication of this code in each controller:
if(!e.status) {
res.status(500).json( { error: { code: 'UNKNOWN_ERROR', message: 'An unknown error occurred.' } });
} else {
res.status(e.status).json( { error: { code: e.code, message: e.message } });
}
3) Does the code require other corrections? I'm just learning Node.js and I want to write the rest of the application well.
Yes, you can throw errors from service layer, it is good practice to catch errors with try/catch block in controller
I handle this with a custom error middleware, just use a next function in a catch block.
catch (e) {
next(e)
}
Example of error middleware (for more info check docs, fill free to move a middleware to file)
app.use(function (err, req, res, next) {
// err is error from next(e) function
// you can do all error processing here, logging, parsing error messages, etc...
res.status(500).send('Something broke!')
})
From my point of view it looks good. If you looking for some best practice and tools, try eslint (with AirBnb config for example) for linting, dotenv for a environment variables management, also check Node.js Best Practice
i want to give you an example:
this code in your controller
findCar(idCar)
} catch (error) {
switch (error.message) {
case ErrorConstants.ELEMENT_NOT_FOUND('LISTING'): {
return {
response: {
message: ErrorMessages.ELEMENT_NOT_FOUND_MESSAGE('LISTING'),
},
statusCode,
}
}
default: {
return {
response: {
message: ErrorMessages.UNKNOWN_ERROR_MESSAGE,
},
statusCode,
}
}
}
}
and this code in your service
findCar: async listingId => {
try {
if (some condition) {
throw new Error(ErrorConstants.ELEMENT_NOT_FOUND('LISTING'))
}
return { ... }
} catch (error) {
console.error(error.message)
throw new Error(ErrorConstants.UNKNOWN_ERROR)
}
},
controller is going to catch the service's errors

Make dynamic/re-usable method

I need to use sendMessage in other method.
Like:
SendMessage('abc#expample.com','abc#exapmle.com','subject','body').
I am new to nodejs. Just need some syntax help.
I have tried
SendMessage.makeBody('','','','');
But its giving me error.
TypeError: sendMessage.makeBody is not a function
function sendMessage(auth) {
const raw = makeBody(
'abc#example.com',
'abc#example.com',
'something',
'something');
gmail.users.messages.send({
auth: auth,
userId: 'me',
resource: {
raw: raw
}
}, function(err, response) {
if (err) {
logger.info('The API returned an error: ' + err);
return;
}
logger.info(response);
});
}
module.exports = {
sendMessage
};
function sendMessage(data) {
gmail.users.messages.send({
auth: data.auth,
userId: 'me',
resource: {
raw: data.raw
}
}, function(err, response) {
if (err) {
logger.info('The API returned an error: ' + err);
return;
}
logger.info(response);
});
}
let raw = makeBody('abc#example.com','abc#example.com','something','something');
let data = {raw: raw, auth: auth}
// pass raw and auth both in a object and use it into sendMessage funtion.
sendMessage(data)
module.exports = {
sendMessage
};

How to get user.id from jwt token in Node.js?

In my User controller, I create a token in which I save this user's id when he login to my application.
exports.findOne = (req, res) => {
User.findOne({
where: {
login: req.body.login,
},
})
.then(user => {
if (user) {
if (bcrypt.compareSync(req.body.password, user.password)) {
const token = jwt.sign(
{
id: user.id, // this is the id I need.
},
env.SECRET_KEY,
{
expiresIn: 129600,
},
);
return res.status(200).json({
message: 'Auth successful',
token,
});
}
...
}
})
.catch(err => {
res.status(400).json({ error: err });
});
};
Now in another controller I would like to read this id and use it for my purpose. How can I get to it?
const loginId = '?'; // here I want to give it to id
Bill.update(
{
available_funds: available_funds - amountMoney,
},
{ where: { id_owner: loginId } },
).then(() => {
res.status(200).send(`ok`);
});
Make a middleware which checks the incoming token before forwarding to your update route.
This middleware should be responsible for validating the incoming token which you pass from the client side code after logging in (storing token in cookies is commonly practiced).
Now in your middleware, you can do something similar to this:
app.use(function(req,res,next) {
JWT.verify(req.cookies['token'], 'YOUR_SECRET', function(err, decodedToken) {
if(err) { /* handle token err */ }
else {
req.userId = decodedToken.id; // Add to req object
next();
}
});
});
Then, finally in your upcoming controller, you can access the id from the request object:
const loginId = req.userId;
Bill.update(
{
available_funds: available_funds - amountMoney,
},
{ where: { id_owner: loginId } },
).then(() => {
res.status(200).send(`ok`);
});
You don't need to add extra codes. To access the userId use this:
req.payload.id

angular2 http post request to get res.json()

I'm currently making simple user-authentication app.
now I'm done with backend process with node js and passport.
what I've done was returning json response if authentication goes well or not.
router.post('/register', (req, res) => {
if(!utils.emailChecker(req.body.username)) {
return res.status(403).json({
error: "Invalid Username",
code: 403
});
}
if(!utils.passwordChecker(req.body.password)) {
return res.status(403).json({
error: "Invalid Password",
code: 403
});
}
//mysql query : variables must be inside "" or '';
let sql = `SELECT * FROM users WHERE username="${req.body.username}"`;
connection.query(sql, (err, result) => {
if(err) throw err;
if(utils.duplicateChecker(result)) {
return res.status(409).json({
error: "Username Exist",
code: 409
});
} else {
hasher({password: req.body.password}, (err, pass, salt, hash) => {
let user = {
authId: 'local: '+req.body.username,
username: req.body.username,
password: hash,
salt: salt,
displayName: req.body.displayName
};
let sql = 'INSERT INTO users SET ?';
connection.query(sql, user, (err, rows) => {
if(err) {
throw new Error("register error!");
} else {
req.login(user, (err) => {
req.session.save(() => {
return res.json({ success: true });
});
});
}
});
});
}
});
});
As you can see above, every time request makes error or goes perfect, json that contains error & code or success property is returned.
What I want to do is that getting these jsons via http service of angular2.
#Injectable()
export class UserAuthenticationService {
private loginUrl = "http://localhost:4200/auth/login";
private registerSuccessUrl = "http://localhost:4200/auth/register";
headers = new Headers({
'Content-Type': 'application/json'
});
constructor(private http: Http) { }
/*
body: {
username,
password,
}
*/
logIn(user: Object) {
return this.http
.post(this.registerSuccessUrl, JSON.stringify(user),
{ headers: this.headers });
}
What I've tried is this way. Make http post request using backend url.
and implement function on AuthComponent.
export class AuthComponent {
username: string = '';
password: string = '';
remembered: boolean = false;
submitted = false;
constructor(private userAuthenticationService: UserAuthenticationService) {}
onsubmit() {
this.userAuthenticationService.logIn({ username: this.username, password: this.password });
this.submitted = true;
}
}
But result is I just get json object on screen. { success: true }!
How can I get this json object thru http call and make use of 'success' property?
You are not using the server's response.
onsubmit() {
this.userAuthenticationService
.logIn({ username: this.username, password: this.password })
.subscribe(result => {
//here check result.success
}, error => console.error(error));
this.submitted = true;
}
The Http calls are asynchronous. Hence, using something like :
const data =this.userAuthenticationService.logIn({ username: this.username, password: this.password }); would not work. Rather subcribe to the response like this :
this.userAuthenticationService.logIn({ username: this.username, password: this.password }).subscribe(
data => {
this.submitted = data.success;
});
Here data is the response object from the server.

Categories

Resources