We will use a login form to show why adding catch in all Axios calls is so important. When users use a login form there needs to be feedback if they submit the wrong username and/or password. Without a catch in your Axios call, you won't get a response unless the response status is 200. Any other response status you will only get an error in your browser console like the image below:
Without a catch, you can't alert the user that there's an issue and the user doesn't know what the problem is.
Let's take a look at the React code of a login form handleSubmit function.
In this function we are getting the user's inputs and storing them in a user object. Then we send that object in the request body to the server route /users/login.
axios.post('/users/login', user)
.then(res => {
props.loggedIn(res.data)
})
.catch(error => {
console.log({
error,
'error status': error.response.status,
'error response': error.response.data
})
alert('Authentication failed')
})
Now let's look at the server code that receives this request. We store the email and password that was sent in the body of the request in variables.
Then we search the database for a user using the email we received; this is the first place that might send back an error. If we have a catch in the axios call we can send back json with the error code, and use it to inform the user that 'Authentication failed' by showing an alert. Now the user can check their inputs for errors.
If we find a user in the database we can move forward to checking if the password is correct. We are using bcrypt to hash our password. Bcrypt provides the bcrypt.compare function to check if the password the user submits matches the hashed password in the database. If the password doesn't match, bcrypt.compare will return false and we can again send json with 'Authentication failed'.
With the catch in place we receive the following response in the browser console:
When building a login route you want to return the same response if the username, password or both are wrong. This will prevent giving someone the ability to find usernames in your database and using a word list to hack into your user's accounts.
router.route('/login').post((req, res) => {
const email = req.body.email
const password = req.body.password
User.findOne({email: email})
.then(user => {
if(!user) {
res.status(400).json('Authentication failed')
} else {
bcrypt.compare(password, user.hashPW, (err, result) => {
if (result === true) {
const refreshToken = makeRefreshToken(user)
const accessToken = makeAccessToken(user)
res.setHeader('Cache-Control', 'private')
res.cookie('refreshToken', refreshToken, { maxAge: 6000000, httpOnly: true})
res.cookie('accessToken', accessToken, { maxAge: 9000000, httpOnly: true })
res.status(200).send({userId: user._id})
} else {
res.status(400).json('Authentication failed')
}
})
}
})
.catch(err => res.status(400).json('Error: ' + err))
})
If the password matches we can return a status of 200 and send the user id back to the React app where we call the loggedIn function with the res.data containing the user id as a parameter.
Top comments (2)
Keep 'em coming! Great article matt!
Thanks Pablo!