Unexpected Server Errors

Keep on Learning!

If you liked what you've learned so far, dive in!
Subscribe to get access to this tutorial plus
video, code and script downloads.

Start your All-Access Pass
Buy just this tutorial for $12.00

With a Subscription, click any sentence in the script to jump to that part of the video!

Login Subscribe

Our /api/purchases API endpoint already has validation rules on it. Yay! Look at the interactive docs and hit "Try it out". I'll empty a couple of fields... and set purchaseItems to an empty array. Hit Execute.

Yea! A 400 error with a really nice structure for the validation error messages. We'll definitely be able to use that in Vue.

If you're a Symfony user, these validation rules are set as normal constraints inside of the Purchase class, like @Assert\NotBlank.

Thinking about how to handle this in Vue, one nice thing is that if the status code is 400, then we know we have some sort of validation error response. If the AJAX call fails with some other status code, that would be an unexpected error. And because this is a super-sensitive checkout form, I want to handle both cases: normal validation errors and unexpected errors.

Handling an Unexpected Error

Let's start by printing a message up here if something weird happens on submit. In the CheckoutForm component, find data and add a new key: serverError set to false.

... lines 1 - 62
<script>
... lines 64 - 67
export default {
name: 'CheckoutForm',
... lines 70 - 79
data() {
return {
... lines 82 - 91
serverError: false,
};
},
... lines 95 - 133
};
</script>

Then, head down to onSubmit and add this.serverError = false at the beginning. If the form is submitted multiple times, this will re-set this back to false each time.

... lines 1 - 62
<script>
... lines 64 - 67
export default {
name: 'CheckoutForm',
... lines 70 - 79
data() {
return {
... lines 82 - 91
serverError: false,
};
},
methods: {
... lines 96 - 109
async onSubmit() {
this.loading = true;
this.serverError = false;
... lines 113 - 131
},
},
};
</script>

Down in catch, let's get to work. First, grab the response with some fancy de-structuring: const { response } = error. Then if response.status - that's the property that holds the integer status code - does not equal 400, we know something weird happened. So let's say this.serverError = true. Else, we have a normal validation error situation... which we'll handle in a few minutes. For now, console.error(response.data).

... lines 1 - 62
<script>
... lines 64 - 67
export default {
name: 'CheckoutForm',
... lines 70 - 79
data() {
return {
... lines 82 - 91
serverError: false,
};
},
methods: {
... lines 96 - 109
async onSubmit() {
this.loading = true;
this.serverError = false;
try {
... lines 115 - 120
} catch (error) {
const { response } = error;
if (response.status !== 400) {
this.serverError = true;
} else {
console.error(response.data);
}
} finally {
this.loading = false;
}
},
},
};
</script>

Up in the template, let's use this new serverError to render a message right inside the form. Add a <div> with v-show="serverError", some classes for styling and a message.

<template>
<div class="row p-3">
<div class="col-12">
<form @submit.prevent="onSubmit">
<div
v-show="serverError"
class="alert alert-danger"
>
Well this is embarrassing ... something went wrong!
Please try again!
</div>
... lines 12 - 65
</form>
</div>
</div>
</template>
... lines 70 - 144

So how can we test this... when our API is so awesome that nothing unexpected ever happens? Great question! We'll just... break the URL! In checkout-service.js, change the URL to something that will 404.

Let's do this! Go to the checkout form and submit. Perfecto! The error message even disappears momentarily each time we submit.

Let's go back and fix that URL... so I don't forget and deploy this to production.

Disabling the Submit Button

Before we handle validation failures, I think we should also disable the submit button so the user can't accidentally hit it multiple times. Our products are so exciting that we've had this problem in the past.

Thanks to our loading data... it's pretty easy. Down on the button, add :disabled="loading".

<template>
<div class="row p-3">
<div class="col-12">
<form @submit.prevent="onSubmit">
... lines 5 - 53
<div class="form-row justify-content-end align-items-center">
... lines 55 - 56
<div class="col-auto">
<button
... lines 59 - 60
:disabled="loading"
>
Order!
</button>
</div>
</div>
</form>
</div>
</div>
</template>
... lines 71 - 145

We can immediately see the results: Bootstrap even has some styling to make it look disabled.

Next: let's handle the most important part: validation errors.

Leave a comment!

This course is also built to work with Vue 3!

What JavaScript libraries does this tutorial use?

// package.json
{
    "devDependencies": {
        "@fortawesome/fontawesome-free": "^5.15.1", // 5.15.1
        "@symfony/webpack-encore": "^0.30.0", // 0.30.2
        "axios": "^0.19.2", // 0.19.2
        "bootstrap": "^4.4.1", // 4.5.3
        "core-js": "^3.0.0", // 3.6.5
        "eslint": "^6.7.2", // 6.8.0
        "eslint-config-airbnb-base": "^14.0.0", // 14.2.0
        "eslint-plugin-import": "^2.19.1", // 2.22.1
        "eslint-plugin-vue": "^6.0.1", // 6.2.2
        "regenerator-runtime": "^0.13.2", // 0.13.7
        "sass": "^1.29.0", // 1.29.0
        "sass-loader": "^8.0.0", // 8.0.2
        "vue": "^2.6.11", // 2.6.12
        "vue-loader": "^15.9.1", // 15.9.4
        "vue-template-compiler": "^2.6.11", // 2.6.12
        "webpack-notifier": "^1.6.0" // 1.8.0
    }
}