Laravel на помилку валідації для запиту з Accept: application/json повертає 422 з тілом:
{
"message": "The email field must be a valid email address. (and 1 more error)",
"errors": {
"email": ["The email field must be a valid email address."],
"items.0.qty": ["The items.0.qty field must be at least 1."]
}
}
Типізований розбір на клієнті:
import * as z from 'zod';
const LaravelValidationError = z.object({
message: z.string(),
errors: z.record(z.string(), z.array(z.string())),
});
export class ValidationError<F extends string = string> extends Error {
constructor(public readonly errors: Partial<Record<F, string[]>>) {
super('Validation failed');
this.name = 'ValidationError';
}
}
if (response.status === 422) {
const body = LaravelValidationError.parse(await response.json());
throw new ValidationError(body.errors);
}
Прив'язка до полів форми. Ключі помилок мають відповідати полям - це можна перевірити типами:
type OrderForm = { email: string; items: { qty: number }[] };
type FieldPath = 'email' | `items.${number}.qty`;
function firstError(errors: Partial<Record<FieldPath, string[]>>, field: FieldPath) {
return errors[field]?.[0];
}
Шаблонний рядковий тип `items.${number}.qty` описує вкладені ключі масивів так само, як їх формує Laravel.
Одна схема для клієнтської й серверної помилки. Якщо форма перевіряється Zod на клієнті, помилки Zod варто привести до того самого формату, що й у Laravel:
const result = OrderFormSchema.safeParse(values);
if (!result.success) {
const { fieldErrors } = z.flattenError(result.error); // { email: ['...'], ... }
}
Тоді компонент форми показує помилки однаково, незалежно від того, звідки вони прийшли.
Що варто врахувати:
- клієнтська перевірка - для зручності, серверна - для захисту. Сервер перевіряє завжди, і його помилки мають показуватися навіть тоді, коли клієнтська схема їх «пропустила» (наприклад, унікальність email);
- ключі вкладених полів: у Laravel
items.0.qty, у бібліотеках форм - частоitems[0].qty. Потрібне перетворення в одному місці; - мова повідомлень: Laravel повертає їх мовою застосунку (
lang/uk/validation.php), клієнтські повідомлення Zod теж треба локалізувати (z.config()з українською локаллю чи власні повідомлення); - Inertia працює інакше: помилки валідації приходять не відповіддю 422, а як props
errorsпісля редиректу, іuseFormрозкладає їх за полями сам.