Розкидані по коду fetch з response.json() as User дають і дублювання, і неперевірені дані. Краще один клієнт, що поєднує запит, обробку помилок і перевірку схемою.
import * as z from 'zod';
export class HttpError extends Error {
constructor(public readonly status: number, public readonly body: unknown) {
super(`HTTP ${status}`);
this.name = 'HttpError';
}
}
export async function apiGet<S extends z.ZodType>(url: string, schema: S): Promise<z.infer<S>> {
const response = await fetch(url, { headers: { Accept: 'application/json' } });
if (!response.ok) {
throw new HttpError(response.status, await response.json().catch(() => null));
}
return schema.parse(await response.json());
}
Використання - тип виводиться зі схеми, жодних as:
const user = await apiGet('/api/users/1', UserSchema);
user.email; // string - і це перевірено
const page = await apiGet('/api/vacancies?page=2', paginated(VacancySchema));
Узагальнена схема для пагінації Laravel:
const paginated = <T extends z.ZodType>(item: T) =>
z.object({
data: z.array(item),
meta: z.object({ current_page: z.number(), last_page: z.number(), total: z.number() }),
});
Чому параметр типу S extends z.ZodType, а не T: тип результату виводиться з переданої схеми. Варіант apiGet<T>(url): Promise<T> без схеми - це прихований as: виклик apiGet<User>(url) виглядає типізованим, але нічого не перевіряє.
Що ще варто додати в клієнт:
- CSRF і cookies для Laravel (
X-XSRF-TOKEN,credentials), заголовокAccept: application/json, щоб помилки валідації приходили як JSON 422; - розбір 422 у типізовану помилку валідації з
errors: Record<string, string[]>; - скасування через
AbortSignal(параметрsignal) і тайм-аут; - POST/PUT з типізованим тілом:
apiPost<In, S>(url, body: In, schema: S).
Пастки:
response.json()на порожній відповіді (204 No Content) кидає помилку - обробляти окремо;- помилка перевірки схеми - це баг контракту між бекендом і фронтендом, а не помилка користувача. Її варто логувати з деталями (
z.prettifyError) у моніторинг, а користувачу показати загальне повідомлення.
Готові варіанти: ky, ofetch з хуками, або генерація клієнта з OpenAPI - тоді й схеми, й типи створюються автоматично.