Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI3 апр. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Как проверить данные с бэкенда
Валидация данных от API критически важна для безопасности и надёжности приложения. Даже если бэкенд отправляет корректные данные, их нужно валидировать на фронте для защиты от ошибок сети, baggy API и потенциальных атак.
1. Zod (современный стандарт)
Это самый популярный выбор для фронтенда на TypeScript:
// lib/validation.ts
import { z } from 'zod';
export const UserSchema = z.object({
id: z.string().uuid(),
name: z.string().min(1).max(100),
email: z.string().email(),
age: z.number().int().positive().optional(),
role: z.enum(['user', 'admin']),
createdAt: z.string().datetime()
});
export type User = z.infer<typeof UserSchema>;
// В компоненте
import { UserSchema } from '@/lib/validation';
async function fetchUser(id: string) {
try {
const response = await fetch(`/api/users/${id}`);
const rawData = await response.json();
// Валидировать данные
const user = UserSchema.parse(rawData);
return user; // Теперь точно типизировано
} catch (error) {
if (error instanceof z.ZodError) {
console.error('Ошибка валидации:', error.errors);
throw new Error('Некорректные данные от API');
}
throw error;
}
}
// Использование
const user = await fetchUser('123');
console.log(user.name); // TypeScript знает, что это строка
2. Type Guards (для простых проверок)
Если не хочешь добавлять Zod, используй type guards:
// Проверка конкретного типа
function isUser(data: unknown): data is User {
if (typeof data !== 'object' || data === null) return false;
const obj = data as Record<string, unknown>;
return (
typeof obj.id === 'string' &&
typeof obj.name === 'string' &&
typeof obj.email === 'string' &&
typeof obj.role === 'string' &&
['user', 'admin'].includes(obj.role)
);
}
// Использование
const user = await fetch('/api/users/123').then(r => r.json());
if (isUser(user)) {
// TypeScript теперь знает, что user это User
console.log(user.name);
} else {
throw new Error('Invalid user data');
}
3. Pydantic + FastAPI (интеграция с бэкендом)
Используй то же самое на фронте, что и на бэкенде. На FastAPI бэкенде:
# backend/app/schemas.py
from pydantic import BaseModel, EmailStr, Field
from datetime import datetime
class UserResponse(BaseModel):
id: str
name: str = Field(..., min_length=1, max_length=100)
email: EmailStr
role: str = Literal['user', 'admin']
created_at: datetime
Фронтенд должен отражать эту же структуру:
// frontend/lib/schemas.ts
const UserSchema = z.object({
id: z.string().uuid(),
name: z.string().min(1).max(100),
email: z.string().email(),
role: z.enum(['user', 'admin']),
created_at: z.string().datetime()
});
4. API оборачивание с автоматической валидацией
Создай обёртку для всех API запросов:
// lib/api.ts
import { z } from 'zod';
export async function apiCall<T>(
url: string,
schema: z.ZodSchema<T>,
options?: RequestInit
): Promise<T> {
try {
const response = await fetch(url, {
...options,
headers: {
'Content-Type': 'application/json',
...options?.headers
}
});
if (!response.ok) {
throw new Error(`API error: ${response.status}`);
}
const data = await response.json();
// Автоматическая валидация
return schema.parse(data);
} catch (error) {
if (error instanceof z.ZodError) {
console.error('Validation failed:', error);
throw new Error('Invalid response from server');
}
throw error;
}
}
// Использование
const user = await apiCall(
'/api/users/123',
UserSchema
);
5. Проверка в компонентах (React)
В самом компоненте добавь защиту:
'use client';
import { useEffect, useState } from 'react';
import { UserSchema, type User } from '@/lib/validation';
export function UserProfile({ userId }: { userId: string }) {
const [user, setUser] = useState<User | null>(null);
const [error, setError] = useState<string | null>(null);
useEffect(() => {
async function load() {
try {
const response = await fetch(`/api/users/${userId}`);
const data = await response.json();
// Валидация перед состоянием
const validatedUser = UserSchema.parse(data);
setUser(validatedUser);
} catch (err) {
setError('Failed to load user');
}
}
load();
}, [userId]);
if (error) return <div>Error: {error}</div>;
if (!user) return <div>Loading...</div>;
return <div>
<h1>{user.name}</h1>
<p>{user.email}</p>
</div>;
}
6. Обработка ошибок от API
От бэкенда может прийти ошибка - её тоже нужно парсить:
const ErrorResponseSchema = z.object({
error: z.object({
code: z.string(),
message: z.string(),
details: z.record(z.string(), z.any()).optional()
})
});
async function apiCall(url: string, options?: RequestInit) {
const response = await fetch(url, options);
const data = await response.json();
if (!response.ok) {
try {
const error = ErrorResponseSchema.parse(data);
throw new Error(error.error.message);
} catch {
throw new Error('Unknown error from server');
}
}
return data;
}
Best Practices для валидации
- Всегда валидируй данные от API, даже от своего же сервера
- Используй Zod для продакшена - это инвестиция в качество
- Типизируй результаты валидации (z.infer)
- Обёртывай все API запросы в одной функции
- Обрабатывай ошибки валидации отдельно от сетевых ошибок
- Логируй ошибки валидации для дебага
- Синхронизируй схемы фронта и бэкенда
// Полный пример обёртки
export async function fetchData<T>(
url: string,
schema: z.ZodSchema<T>
): Promise<Result<T>> {
try {
const response = await fetch(url);
if (!response.ok) return { ok: false, error: 'Network error' };
const raw = await response.json();
const validated = schema.safeParse(raw);
if (!validated.success) {
return { ok: false, error: 'Invalid data' };
}
return { ok: true, data: validated.data };
} catch (error) {
return { ok: false, error: String(error) };
}
}
Это критический навык, который показывает понимание надёжности и безопасности фронтенда!