I'm scratching my head trying to figure out the best way to handle errors from specific user actions. I'm using Express as my web server and even though it works, for the most part, I am getting not-so-useful, generic error messages. For instance, in the code below, I get the Request failed with status code 400 error message on the client side for the first two conditions/exceptions in the try block.
How do I approach this in the following example?
Express Server-side Controller
async function voteInPoll (req, res) {
const { category, pollId } = req.params;
const { name, choiceId, voterId } = req.body;
try {
const poll = await Poll.findById(pollId);
// Check if user has already voted in poll
const hasVoted = poll.votedBy.some(voter => voter.equals(voterId));
if (!voterId) { // Check if user is authenticated
res
.sendStatus(400)
.json({ message: 'Sorry, you must be logged in to vote' });
} else if (voterId && hasVoted) {
res
.sendStatus(400)
.json({ message: 'Sorry, you can only vote once' });
} else {
const choice = await poll.choices.id(choiceId);
const votedChoice = { name, votes: choice.votes + 1 };
await choice.set(votedChoice);
await poll.votedBy.push(voterId);
poll.save();
res
.sendStatus(200)
.json({
message: 'Thank you for voting. Find other polls at: ',
poll,
});
}
} catch (error) {
throw new Error(error);
}
}
React/Redux Action
export const voteInPoll = (category, pollId, votedItem, voterId) => async dispatch => {
try {
const response = await axios.post(
`http://localhost:3050/polls/${category}/${pollId}/vote`,
{
...votedItem,
voterId,
}
);
dispatch({ type: store.polls.VOTE_SUCCESS, payload: response.data.poll });
} catch (error) {
console.log(error);
dispatch({ type: store.polls.VOTE_FAILURE, payload: error.message });
}
};
Edit
What I find rather bizarre is I get the expected error response sent, as seen below under the Network tab of Chrome's Developer tools.
You should not be using res.sendStatus(statusCode) because of the following as defined in the docs here:
Sets the response HTTP status code to statusCode and send its string representation as the response body.
The key thing about the above is:
and send its string representation as the response body.
So doing: res.sendStatus(400).json({ message: 'Oops 400!'}) will not give you a JSON response which is what you're expecting, but simply display:
Bad Request
Which is the string representation of the 400 HTTP status code: https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#4xx_Client_errors
What you need to do is replace all of your res.sendStatus(..).json(..) with res.status(...).json(...) like so:
if (!voterId) { // Check if user is authenticated
res
.status(400)
.json({ message: 'Sorry, you must be logged in to vote' });
} else if (voterId && hasVoted) {
res
.status(400)
.json({ message: 'Sorry, you can only vote once' });
} else {
// ...
}
and so on.
Related
Node.js CODE
exports.user = async (req, res) => {
try {
const { wallet } = req.body;
if (!wallet) {
res.status(400).json({ error: "Not logged in" });
return;
} else {
user = User.findone(wallet);
// if user is not found then create a new user and mark as loggged In
if (!user) {
User.create({
user: wallet,
});
}
// if user found then create a session token and mark as logged
in
res.send({
user: wallet,
});
}
} catch (error) {
console.log(`ERROR::`, error);
}
};
REACTJs CODE
// post call/update
const axiosCall = async () => {
// core login will give a unique username by fulling a transcation
// core.login i dont have any control
const userAccount = await core.login();
try {
const res = await Axios.post(`${API}/user`, userAccount, dataToken);
setData({
...data,
error: "",
success: res.data.message,
});
} catch (error) {
setData({
...data,
error: error.response.data.error,
});
}
};
Now here the problem occurs when some one could modify userAccount in the front-end or someone could send a body with wallet: anything to my route localhost:3000/api/user
There is no option for me to check if some actually used core.login(); to get the wallet address.
So is there any solution?
I was thinking to allow only my server IP or localhost to hit the route localhost:3000/api/user and is that even possible?
Also there is another issue anyone could modify userAccount in front-end.
I have a register function inside my Express application to create a new user. Inside this function there are a few tasks: create the user in Auth0, send an email, send a response to the client.
I want to be able to catch the errors coming from Auth0 or Postmark to send back specific errors to the client and log them to the console. I though I could achieve this by adding a catch to an await function (I want to avoid a waterfall of .then() and .catch() blocks). This sends the error to the client but doesn't stop the code from executing. The email part is still trying to execute while the user object is undefined and I'm getting the error Cannot set headers after they are sent to the client.
How can I fix this by keeping the async/await functionality and keep the seperate error handling for each action?
Register function
export const register = asyncHandler(async (req, res, next) => {
// Create user in Auth0
const user = await auth0ManagementClient.createUser({
email: req.body.email,
password: generateToken(12),
verify_email: false,
connection: 'auth0-database-connection'
}).catch((error) => {
const auth0_error = {
title: error.name,
description: error.message,
status_code: error.statusCode
}
console.log(auth0_error);
if(error.statusCode >= 400 && error.statusCode < 500) {
return next(new ErrorResponse('Unable to create user', `We were unable to complete your registration. ${error.message}`, error.statusCode, 'user_creation_failed'));
} else {
return next(new ErrorResponse('Internal server error', `We have issues on our side. Please try again`, 500, 'internal_server_error'));
}
});
// Send welcome mail
await sendWelcomeEmail(user.email)
.catch((error) => {
const postmark_error = {
description: error.Message,
status_code: error.ErrorCode
}
console.log(postmark_error);
if(error.statusCode >= 400 && error.statusCode < 500) {
return next(new ErrorResponse('Unable to send welcome email', `We were unable to send a welcome email to you`, error.statusCode, 'welcome_email_failed'));
} else {
return next(new ErrorResponse('Internal server error', `We have issues on our side. Please try again`, 500, 'internal_server_error'));
}
});
res.status(201).json({
message: 'User succesfully registered. Check your mailbox to verify your account and continue the onboarding.',
data: {
user
}
});
});
asyncHandler.js
const asyncHandler = fn => ( req, res, next) => Promise.resolve(fn(req, res, next)).catch(next);
export default asyncHandler;
I'd use try/catch blocks, but declare the user variable outside the try scope.
async function handler(req, res, next) {
let user;
try {
user = await auth0ManagementClient.createUser(...);
} catch (error) {
return next(new ErrorResponse(...));
}
try {
await sendWelcomeEmail(user.email);
} catch (error) {
return next(new ErrorResponse(...));
}
res.status(201).json(...);
}
return will only terminate the current function. Here, the function that gets terminated by the return is the .catch() callback.
In your example and if you want to stick to using Promise.then().catch() you can check for the user value as the catch() callback will return its value in it.
The easier way would be to use try/catch blocks to interrupt the whole controller with the return statement.
1.I'm working on an backend API but at some point I need to get user data from another API. I am trying to use Axios to make http request in order to do that. The request return the result in the browser as expected but the problem is that I can't display console log in the terminal. It doesn't show anything even though I asked the program to do so. Is there a problem probably with my code?
2.Error message =>>> POST http://localhost:8000/api/register 400 (Bad Request) Error: Request failed with status code 400`
const handleSubmit = async () => {
//e.preventDefault();
try
{
// console.log(name, email, password, secret);
const { data } = await axios.post("http://localhost:8000/api/register", {
name,
email,
password,
secret,
});
setOk(data.ok); //useState component
}
catch (error) {
**strong text**
console.log(error.response.data);
}
}
import User from '../models/user'
//import{ hashPassword, comparePassword } from '../helpers/auth'
export const register = async (req,res) => {
//console.log('Register endpoint =>', req.body)
//to make this work make express.json is applied in the above middleware
//console.log error to debug code
const {name, email, password, secret} = req.body;
//validation
if(!name) return res.status(400).send('Name is required')
if(!password || password.length < 6) return res.status(400).send('Password is
short
or password is not entered')
if(!secret) return res.status(400).send('Answer is required')
//The above code is for validation purpose to make sure data is correctly
entered
const exist = await User.findOne({email })
if(exist) return res.status(400).send('Email is taken')
}
.catch(error => {
console.log(error)
})
May be catching error on your axios is wrong try this
I am new to React and Next.js I am trying to send an email via sendGrid from a contact form. I have combined a couple of tutorials to get what I want but I am clearly not understanding something.
Using Next.js I have a contact form /pages/contact.js onSubmit calls /pages/api/sendMail which imports a function sendMailToMe() from utils/sendMailToMe.js
The code works and sends the email but I cannot seem to pass the result from sendMailToMe() back to sendMail.js
/utils/sendMailToMe.js
const sendMailToMe = async (
fullName,
formMessage,
email
) => {
const mail = require('#sendgrid/mail');
mail.setApiKey(SENDGRID_API_KEY);
const msg = {
to: 'mike#mydomain.com',
from: 'mike#mydomain.com',
templateId: 'd-3481ff06ea924128baa7c16a5a7f4840',
dynamicTemplateData: {
subject: 'Testing Templates',
fullName: fullName,
message: formMessage,
},
};
mail.send(msg)
.then((response) => {
console.log('in response')
console.log(response[0].headers)
return response[0].statusCode
})
.catch((error) => {
console.log("there was an error")
console.error(error)
return 'test'+error
})
//return response;
}
export { sendMailToMe };
This is imported and called as follows to pages/api/sendMail.js
import { sendMailToMe } from "../../utils/sendMailToMe";
export default async function handler(req, res) {
if (req.method === "POST") {
const { email, fullName, message,test } = req.body;
if (
typeof (email || fullName || test || message) === "undefined"
) {
console.log(" ************* Invalid Data received ************ ");
return res
.status(400)
.send({ error: "bad request, missing required data!" });
} else {
// Data received as expected
console.log('Calling sendMailToMe')
const sendGridResult = await sendMailToMe(
fullName,
message,
email
)
.then((response)=>{console.log(response)}) //res.status(200).send({test: 'test'})})
.catch((err) =>{ console.log(err)})//res.status(400).send({error:"Error in Sendgrid", errMsg:err})})
// API returns here regardless of outcome
res.status(200).send({test: 'returning here on error or success'})
}
}else{
res.status(400).send({ error: "Must use POST method" });
}
//res.status(400).send({ error: "bad request somehow" });
}
I am trying to get the result of mail.send() back to the api so I can return the proper response. Right now sendMail.js returns 200 even if mail.send() fails. The console logs the response or error in sendMailToMe() but I can't get the response or error back to sendmail.js. Any pointers in the right direction appreciated.
i am using graph api javascript example from here https://learn.microsoft.com/en-us/graph/api/user-list-joinedteams?view=graph-rest-beta&tabs=javascript
and my code is like:
async function(req, res) {
if (!req.isAuthenticated()) {
// Redirect unauthenticated requests to home page
res.redirect('/')
} else {
let params = {
active: { calendar: true }
};
// Get the access token
var accessToken;
try {
accessToken = await tokens.getAccessToken(req);
console.log("access token is:", accessToken)
} catch (err) {
req.flash('error_msg', {
message: 'Could not get access token. Try signing out and signing in again.',
debug: JSON.stringify(err)
});
}
if (accessToken && accessToken.length > 0) {
try {
console.log("vik testing stuff12 for teams")
const user = await graph.getTeams(accessToken)
console.log("graph me:::", user)
} catch (err) {
req.flash('error_msg', {
message: 'Could not fetch events',
debug: JSON.stringify(err)
});
}
} else {
req.flash('error_msg', 'Could not get an access token');
}
res.render('calendar', params);
}
}
getTeams is
getTeams: async function(accessToken) {
const client = getAuthenticatedClient(accessToken);
const events = await client
.api('/me/joinedTeams')
.version('beta')
.get();
return events;
}
this prints no results and no error. if I replace 'me/joinedTeams' to just 'me' then it returns logged in user details.
You can got a response successfully, so it seems no error with your code as you said if you call https://graph.microsoft.com/v1.0/me you can get user information.
And I tried to call this API using my account(my account hasn't joined any Teams), and got response like below, so if you got the same response as mine, perhaps you need to check if you have joined any Teams:
On the other hand, following the document, this API needs several permissions. So please obtain your access token when debug and use JWT tool to decrypt it to check if the access token have enough scope.
And I used the same request and got Teams information after adding my account to a team.