Image

token-based-authentication

6 min read
Last update: December 19, 2021

Handling Errors

In this lesson, we’ll be taking a look at handling some of the errors that can happen when users attempt to register or log in to our app. So let’s think about the situations that we should be checking for.

First, we want to make sure that when a user tries to log in with invalid credentials (with the wrong password, for example) we display an error. We’ll handle that inside of our LoginUser component. Additionally, when a user tries to register an account with our app, a few things could go wrong. They might already have an account and try to create a second one with an email that already exists. Or they may be trying to create an account with an invalid password (too short, for example). We will need to display errors then, too, within our RegisterUser component.


Understanding the Server Behavior

Before we add the features I just mentioned, it will be helpful if we understand what the server ought to be doing so that our front end Vue app can respond appropriately.

When a user logs in, we’ll need our backend to check that the user’s credentials (in our case: the email and password) match a record in our database. Remember that in our mock backend, we only store one user at a time, so we are simply checking if the user data that is stored matches the credentials our user is logging in with. If not, the server should send back an error with the status code of 401.

You can observe that behavior in our mock server.js file:

server.js

app.post('/login', (req, res) => {
const userDB = fs.readFileSync('./db/user.json') // reading db
const userInfo = JSON.parse(userDB)
if ( // check if user credentials exists in db
req.body &&
req.body.email === userInfo.email &&
req.body.password === userInfo.password
) {
const token = jwt.sign({ userInfo }, 'the_secret_key')
res.json({
token,
email: userInfo.email,
name: userInfo.name
})
} else {
res.status(401).json({ error: 'Invalid login. Please try again.'}) // send error if credentials don't match record
}
})

Great. Now what do you think the /register endpoint ought to do?


Since we don’t want a user creating multiple accounts with the same email, we’ll need to check that the user’s registration credentials do not match what already exists in our database. We also need to ensure they are giving us a strong enough password. In a production solution, your team may be using a validation library and have a custom validation solution on your backend. But for simplicity’s sake, this is how we are handling that “validation” in our mock server.js file:

server.js

app.post('/register', (req, res) => {
...
var errorsToSend = [] // array to collect errors

if (dbUserEmail === user.email) { // check to see if email already exists in db
errorsToSend.push('An account with this email already exists.')
}
if (user.password.length < 5) { // validate password is in correct format errorsToSend.push('Password too short.') } if (errorsToSend.length> 0) { // check if there are any errors
    res.status(400).json({ errors: errorsToSend }) // send errors back with status code
    } else {
    // success
    }
    ...
    })
    ```

    Now that we understand how our dummy server is behaving, and what triggers those status codes to be sent back, we can start adding to our Vue app to receive and display them.

    ---

    ## Handling the Login Error

    Inside of our **LoginUser** component, in our `login` method, we can add the ability to `catch` the error the server sends back to us.

    **src/views/LoginUser.vue**

    ```
    login () {
    this.$store
    .dispatch('login', {
    email: this.email,
    password: this.password
    })
    .then(() => {
    this.$router.push({ name: 'dashboard' })
    })
    .catch(err => {
    // now what?
    })
    }
    ```

    Once an error is caught, we can add it to a new `error` property on our component’s data, like so:

    **src/views/LoginUser.vue**

    ```
    <script>
        export default {
            data() {
                return {
                    ...
                    error: null
                }
            },
            methods: {
                login() {
                    ...
                    .catch(err => {
                        this.error = err.response.data.error
                    })
                }
            }
        }
    </script>
    ```

    Now that we are locally storing that error status code, we can display that message in our template.

    Below our `button`, we’ll add:

    **src/views/LoginUser.vue**

    ```
    <template>
        ...
        <p>{{ error }}</p>
        ...
    </template>
    ```

    If our user attempts to login with invalid credentials, they’ll now be met with an error message.

    Great. Now we can head into our **RegisterUser** component and repeat this process.

    ---

    ## Handling the Register Errors

    Inside of our **RegisterUser** component, we will `catch` the error the same way we just did, but now in the `register` method. We’ll also be adding that response error to a new `errors` property on our component’s data.

    **src/views/RegisterUser.vue**

    ```
    <script>
        export default {
            data() {
                return {
                    ...
                    errors: null
                }
            },
            methods: {
                register() {
                    ...
                    .catch(err => {
                        this.errors = err.response.data.errors
                    })
                }
            }
        }
    </script>
    ```

    Now we can add to our template to display the error messages we’ve collected. We’ll do that by iterating over them with a `v-for`.

    **src/views/RegisterUser.vue**

    ```
    <template>
        ...
        <ul>
            <li v-for="(error, index) in errors" :key="index">
                {{ error }}
            </li>
        </ul>
        ...
    </template>
    ```

    Great, now when a user logs in with an already-registered email or too short of a password, they’ll be notified.

    ---

    ### Side Notes:

    1. In addition to whatever validation you are doing on the server-side, you’ll also want to add validation to these forms on the front end. We teach you how to do that in our [Next Level Vue course](https://www.vuemastery.com/courses/next-level-vue/form-validation-with-vuelidate).

    2. You’ll want to handle when the server API is down. We also teach this in [Next Level Vue course](https://www.vuemastery.com/courses/next-level-vue/404-error-handling).

    3. Depending on the level of security your app requires, the message here will vary. For example, if you don’t want a hacker knowing the email they just input exists in your database, you probably shouldn’t tell them: “The email already exists.” You and your team can decide on the best message to display for your needs.

    4. You may be wondering at this point why we’re only handling these errors at the component level and not using Vuex. These errors are presently not of concern to our entire app, so we don’t need to share them with any other components. We’re better off simply keeping this functionality of catching, storing and displaying the errors within the components that care about them.


    ---

    ## Let’s ReVue

    We’ve now added the ability for our user interface to `catch` errors when a user logs in or registers with invalid credentials, and display messages to our user accordingly. We also looked at the behavior that ought to happen on the backend to get this working on the front end.

    In the next lesson, we’ll look at how to automatically log users back into your app.