Коли валідація Laravel не проходить у запиті, що очікує JSON, сервер відповідає статусом 422 і тілом:
{
"message": "The email has already been taken. (and 1 more error)",
"errors": {
"email": ["The email has already been taken."],
"items.0.qty": ["The items.0.qty field must be at least 1."]
}
}
Ключі - назви полів, для вкладених даних - через крапку. Значення - масив повідомлень.
Обробка в Vue:
<script setup lang="ts">
import { reactive, ref } from 'vue'
import axios from 'axios'
const form = reactive({ name: '', email: '' })
const errors = ref<Record<string, string[]>>({})
async function submit() {
errors.value = {}
try {
await axios.post('/api/users', form, { headers: { Accept: 'application/json' } })
} catch (e) {
if (axios.isAxiosError(e) && e.response?.status === 422) {
errors.value = e.response.data.errors
return
}
throw e // інші помилки - далі, до глобального обробника
}
}
</script>
<template>
<input v-model="form.email" :aria-invalid="!!errors.email" />
<p v-if="errors.email" class="error">{{ errors.email[0] }}</p>
</template>
З fetch код схожий, але fetch не кидає виняток на 422 - перевіряти треба response.status самостійно, а тіло читати через await response.json().
Важливі деталі:
- заголовок
Accept: application/jsonобов'язковий: без нього Laravel вважає запит звичайним переходом форми й відповідає редиректом назад з помилками в сесії, а не JSON; - очищайте помилки перед новою відправкою, інакше старі повідомлення лишаються на екрані;
- прибирайте помилку поля при його зміні -
watchна поле чи очищення в@input- користувач бачить, що виправлення прийнято; - вкладені ключі (
items.0.qty) треба зіставити з полем у списку - зберігайте помилки за тим самим шаблоном назв; - 401 і 419 - це не валідація: 401 - не автентифікований, 419 - прострочений CSRF-токен (для SPA на Sanctum спершу потрібен запит до
/sanctum/csrf-cookie).
Типізація: опишіть тип відповіді 422 ({ message: string; errors: Record<string, string[]> }) один раз і використовуйте в усіх формах - через спільну функцію чи composable useServerErrors().