Handling errors with VUE and Laravel

Murilo Livorato
5 min readJan 7, 2024

--

Today, I will share with you the way that I handle erros with Vue + Laravel .

Handling errors from a Laravel API in a Vue.js application involves making API requests, capturing potential errors, and displaying meaningful messages to the user. Below is a step-by-step tutorial to guide you through this process:

Step 1: Set up Laravel API

Make sure your Laravel API is properly set up and configured to return meaningful error responses.

Set the API router -

Route::post('/register',  [AuthUserController::class, 'register']);

Set the Controller -

public function register(RegisterUserRequest $request) {
return response()->json(null, 200);
}

And Finally set The Request

namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;

class RegisterUserRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return true;
}

/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{

return [
'name' => 'required|string|min:3|max:32',
'email' => 'required|email|unique:users,email',
'password' => 'required|string|min:6|max:8',
'password_confirmation' => 'required|string|same:password',
];

}
}

Step 2— Start a Vue Project

npm create vue@latest

Install Bulma css

npm install bulma
npm install -D sass

Create the HTML

  <div class="container">
<form method="POST" @submit.prevent="storeForm">
<fieldset>
<div class="field is-horizontal">
<div class="field-label">
<label class="label">Name</label>
</div>
<div class="field-body">
<div class="field">
<div class="control">
<input class="form-control ipone" name="name" type="text" v-model="form.name" >
<p class="error-msg" v-if="errors.has('form.name')" v-text="errors.get('form.name')"></p>
</div>
</div>
</div>
</div>
<div class="field is-horizontal">
<div class="field-label">
<label class="label">E-mail</label>
</div>
<div class="field-body">
<div class="field">
<div class="control">
<input class="form-control ipone" name="email" type="text" v-model="form.email" >
<p class="error-msg" v-if="errors.has('form.email')" v-text="errors.get('form.email')"></p>
</div>
</div>
</div>
</div>
<div class="field is-horizontal">
<div class="field-label">
<label class="label">Password</label>
</div>
<div class="field-body">
<div class="field">
<div class="control">
<input class="form-control ipone" name="password" type="password" v-model="form.password" >
<p class="error-msg" v-if="errors.has('form.password')" v-text="errors.get('form.password')"></p>
</div>
</div>
</div>
</div>
<div class="field is-horizontal">
<div class="field-label">
<label class="label">Password Confirmation</label>
</div>
<div class="field-body">
<div class="field">
<div class="control">
<input class="form-control ipone" name="password" type="password" v-model="form.password_confirmation" >
<p class="error-msg" v-if="errors.has('form.password_confirmation')" v-text="errors.get('form.password_confirmation')"></p>
</div>
</div>
</div>
</div>
<div class="field is-horizontal">
<div class="field-label is-small">
</div>
<div class="field-body">
<div class="field">
<div class="control">
<submit-btn :processloading="processingForm"
faiconposition="left"
stylebutton="btn_cl_left btn-green-md1"
textbutton="Register" ></submit-btn>
</div>
</div>
</div>
</div>
</fieldset>
</form>
</div>
</template>

Set the Store with Pinia -

import { defineStore } from 'pinia'
import axios from 'axios'
export const useRegisterUserStore = defineStore('registerUserStore', {
state: () => {
return {
// PAGE INFO
pageInfo: {
STORE_URL: 'http://localhost:8080/api/register'
},
}
},
actions: {
async store (data) {
try {
const response = await axios.post(this.pageInfo.STORE_URL, data)
// RETURN REQUEST
return response.data
} catch (error) {
console.log('my eerorr', error)
throw error.response.data
}
}
},
getters: {
}
})

And Finally we gonna create our Error Class

import { ref } from 'vue'
export default function () {
const errors = ref({})
// GET ERRRO
const get = (field) => {
// IF IS AN ARRAY ERROR
if (field.includes('.')) {
const splitString = field.split('.')

// IT IS AN ARRAY FORM -> FORM.IMAGE_GALLERY.0.IMAGES
if (splitString.length === 4) {
// LARAVEL FORM ERROR
const errorOne = errors.value[splitString[0]][splitString[1]][splitString[2] + '.' + splitString[3]]
if (errorOne) {
return errorOne
}

if (errors.value[splitString[0]][splitString[1]][splitString[2]][splitString[3]]) {
return errors.value[splitString[0]][splitString[1]][splitString[2]][splitString[3]][0]
}
}

// IT IS AN ARRAY FORM -> FORM.IMAGE_GALLERY.IMAGES
if (splitString.length === 3) {
// LARAVEL FORM ERROR
const errorOne = errors.value[splitString[0]][splitString[1] + '.' + splitString[2]]
if (errorOne) {
return errorOne
}

if (errors.value[splitString[0]][splitString[1]][splitString[2]]) {
return errors.value[splitString[0]][splitString[1]][splitString[2]][0]
}
}

// ADD ERROR INSIDE ARRAY LIST -> FORM.TITLE
if (errors.value[splitString[0]][splitString[1]]) {
return errors.value[splitString[0]][splitString[1]][0]
}

return false
}

// VALIDATE WITH NO FORM REFRENCE -> TITLE
if (errors.value[field]) {
return errors.value[field][0]
}
}

const record = (errorData, list = null) => {
clear()
if (list) {
errors.value = [list]
errors.value[list] = errorData
return
}

errors.value = errorData
}

// RESET RECORDS
const reset = () => {
errors.value = {}
}

// ANY
const any = () => {
return Object.keys(errors.value).length > 0
}

// HAS
const has = (field) => {
// IF IS AN ARRAY ERROR
if (field.includes('.')) {
const splitString = field.split('.')

if ({}.hasOwnProperty.call(errors.value, splitString[0])) {
// IT IS AN ARRAY FORM -> FORM.IMAGE_GALLERY.0.IMAGES
if (splitString.length === 4) {
if ({}.hasOwnProperty.call(errors.value[splitString[0]][splitString[1]], splitString[2])) {
// LARAVEL FORM ERROR
const errorOne = errors.value[splitString[0]][splitString[1]][splitString[2] + '.' + splitString[3]]
if (errorOne) {
return errorOne
}

// MY FORM ERROR
return {}.hasOwnProperty.call(errors.value[splitString[0]][splitString[1]][splitString[2]], splitString[3])
}
return false
}

// IT IS AN ARRAY FORM -> FORM.IMAGE_GALLERY.IMAGES
if (splitString.length === 3) {
// LARAVEL FORM ERROR
const errorOne = errors.value[splitString[0]][splitString[1] + '.' + splitString[2]]
console.log('errorOne', errorOne)
if (errorOne) {
return errorOne
}
// MY FORM ERROR

// IT HAS FORM REFERENCE -> FORM.TITLE
const data = {}.hasOwnProperty.call(errors.value[splitString[0]], splitString[1])
if (!data) {
return false
}
return {}.hasOwnProperty.call(errors.value[splitString[0]][splitString[1]], splitString[2])
}

// IT HAS FORM REFERENCE -> FORM.TITLE
return {}.hasOwnProperty.call(errors.value[splitString[0]], splitString[1])
}

return false
}

// DOES NOT HAVA FORM REFERENCE -> TITLE
return {}.hasOwnProperty.call(errors.value, field)
}

// CLEAR
const clear = (field) => {
if (field) delete errors.value[field]

errors.value = {}
}

// VERIFY ERROR AND CLEAR
const verifyErrorAndClear = (field) => {
if (has(field)) {
clearField(field)
}
}

// CLEAR FIELD
const clearField = (field) => {
// HAS ARRAY ERROR
if (field.includes('.')) {
const splitString = field.split('.')
const listName = splitString[0]
const fieldName = splitString[1]

delete errors.value[listName][fieldName]
return
}

delete errors.value[field]
}
return {
errors,
get,
record,
reset,
any,
has,
clear,
verifyErrorAndClear,
clearField
}
}

putting all togueder in the script

import { reactive, ref } from 'vue'
import useErrors from '@/use/Errors'
import SubmitBtn from '@/components/SubmitBtn.vue'
import Swal from 'sweetalert2'
const processingForm = ref(false)

import { useRegisterUserStore } from '@/stores/registerUser'
const storeRegisterUser = useRegisterUserStore()

const form = reactive({ name: '', email: '', password: '', password_confirmation: '' })
const errors = useErrors()

// here process the form
const storeForm = () => {
// PROCESSING
processingForm.value = true

storeRegisterUser.store(form).then(() => {
// PROCESSING
processingForm.value = false
// SUCCESS MESSAGE
new Swal({
icon: 'success',
title: 'The User Was Registered',
showConfirmButton: false,
timer: 2000
}).then(() => {})
}).catch(error => {
// it has errors
// PROCESSING
processingForm.value = false
// now will set those errors
errors.record(error.errors, 'form')
})
}

Final Result

Conclusion

This tutorial provides a basic structure for error handling in a Vue.js application that communicates with a Laravel API. Customize it based on your specific API endpoints and error response structure.

The Git Hub Project -

Check as well how to use the same class, using react -
https://medium.com/@murilolivorato/handling-errors-with-react-and-laravel-616d8a6562e4

Thanks a lot for reading till end. Follow or contact me via:
Github:https://github.com/murilolivorato
LinkedIn: https://www.linkedin.com/in/murilo-livorato-80985a4a/
Youtube : https://www.youtube.com/@murilolivorato1489

--

--