Типобезопасные формы и контракты ответов API
Используйте Zod для вывода типов TypeScript из схем и валидации данных форм и ответов API.
«Типобезопасные формы и контракты ответов API» — бесплатный урок React Academy на CoddyKit. Это урок 4 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения React Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс React Academy содержит 4 уроков всего.
Почему типобезопасные структуры данных важны
Формы и ответы API — это границы, через которые в приложение поступают данные из ненадёжных источников. Zod позволяет определять схемы, которые одновременно проверяют данные во время выполнения и выводят типы TypeScript.
Основы схем Zod
Определяйте схемы с помощью гибкого API Zod. Используйте z.infer<typeof schema>, чтобы извлечь тип TypeScript.
import { z } from 'zod';
const UserSchema = z.object({
id: z.string().uuid(),
name: z.string().min(1).max(100),
email: z.string().email(),
age: z.number().int().min(0).max(150).optional(),
role: z.enum(['admin', 'user', 'guest']),
});
type User = z.infer<typeof UserSchema>;
// { id: string; name: string; email: string; age?: number; role: 'admin'|'user'|'guest' }React Hook Form с разрешателем Zod
Интегрируйте схемы Zod с React Hook Form для типобезопасной проверки форм — TypeScript знает структуру данных формы и ошибок.
import { useForm } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
const LoginSchema = z.object({
email: z.string().email('Invalid email'),
password: z.string().min(8, 'At least 8 characters'),
});
type LoginData = z.infer<typeof LoginSchema>;
function LoginForm() {
const { register, handleSubmit, formState: { errors } } = useForm<LoginData>({
resolver: zodResolver(LoginSchema),
});
const onSubmit = (data: LoginData) => {
// data is LoginData — fully typed, already validated
};
}Проверка ответов API
Разбирайте ответы API с помощью Zod, чтобы обнаруживать несоответствия структуры на границе приложения. Если API возвращает неожиданные данные, вы получите подробную ошибку вместо сбоя во время выполнения глубоко внутри компонента.
async function fetchUser(id: string): Promise<User> {
const res = await fetch(`/api/users/${id}`);
const json = await res.json();
return UserSchema.parse(json); // throws ZodError if shape is wrong
}Безопасный разбор с safeParse
Используйте schema.safeParse(), чтобы получить объект результата вместо исключения, — это особенно удобно для проверки форм, когда нужно показать ошибки пользователю.
const result = UserSchema.safeParse(formData);
if (!result.success) {
const errors = result.error.flatten().fieldErrors;
// { name: ['Must be at least 1 character'], email: ['Invalid email'] }
return errors;
}
const user = result.data; // User — fully typedДискриминированные объединения с Zod
Используйте z.discriminatedUnion, чтобы описывать ответы API с разной структурой в зависимости от признака успешного выполнения или ошибки.
const ApiResponse = z.discriminatedUnion('ok', [
z.object({ ok: z.literal(true), data: UserSchema }),
z.object({ ok: z.literal(false), error: z.string(), code: z.number() }),
]);
type ApiResult = z.infer<typeof ApiResponse>;
// { ok: true; data: User } | { ok: false; error: string; code: number }Преобразования в Zod
Используйте .transform(), чтобы приводить данные к нужному типу или изменять их структуру во время разбора — например, преобразовывать строку с датой в объект Date.
const DateSchema = z.string().transform(s => new Date(s));
// Input: '2024-01-15' → Output: Date object
const EventSchema = z.object({
title: z.string(),
date: z.string().pipe(z.coerce.date()),
});
type Event = z.infer<typeof EventSchema>;
// { title: string; date: Date }Повторное использование схем
Создавайте схемы для создания и обновления на основе базовой схемы, чтобы избежать дублирования.
const UserSchema = z.object({ name: z.string(), email: z.string().email() });
// For creation: add password
const CreateUserSchema = UserSchema.extend({ password: z.string().min(8) });
// For update: all fields optional
const UpdateUserSchema = UserSchema.partial();
type CreateUser = z.infer<typeof CreateUserSchema>;
type UpdateUser = z.infer<typeof UpdateUserSchema>;Серверное действие с Zod
Проверяйте данные формы в серверном действии Next.js с помощью Zod до обращения к базе данных.
async function createPost(formData: FormData) {
'use server';
const schema = z.object({ title: z.string().min(3), body: z.string().min(10) });
const result = schema.safeParse(Object.fromEntries(formData));
if (!result.success) return { errors: result.error.flatten().fieldErrors };
await db.post.create({ data: result.data });
revalidatePath('/blog');
redirect('/blog');
}Общие схемы для фронтенда и бэкенда
Экспортируйте схемы Zod из общего пакета, чтобы фронтенд и бэкенд использовали одну и ту же логику проверки — одна схема служит единым источником истины.
// packages/schemas/src/user.ts
export const CreateUserSchema = z.object({ ... });
export type CreateUser = z.infer<typeof CreateUserSchema>;
// Frontend imports:
import { CreateUserSchema, CreateUser } from '@company/schemas';
// Backend imports:
import { CreateUserSchema } from '@company/schemas';Сообщения об ошибках Zod
Настраивайте сообщения об ошибках для каждого поля, чтобы улучшить взаимодействие с пользователем: Zod автоматически передаёт их форме для отображения ошибок через средство разрешения.
const RegistrationSchema = z.object({
username: z.string()
.min(3, 'Username must be at least 3 characters')
.max(20, 'Username cannot exceed 20 characters')
.regex(/^[a-z0-9_]+$/, 'Only lowercase letters, numbers, and underscores'),
});Быстрая проверка
Что предоставляет z.infer
Итоги
Определяйте схемы Zod один раз и используйте z.infer, чтобы выводить типы TypeScript. Применяйте zodResolver в React Hook Form для проверки с безопасной типизацией. Проверяйте ответы API с помощью Zod на границе системы, используйте safeParse для ошибок, отображаемых пользователю, и делитесь схемами между фронтендом и бэкендом через общий пакет.
Часто задаваемые вопросы
Урок «Типобезопасные формы и контракты ответов API» бесплатный?
Да — полный текст урока «Типобезопасные формы и контракты ответов API» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс React Academy, подпишись на CoddyKit PRO. Курс React Academy содержит 4 уроков всего.
Чему я научусь в уроке «Типобезопасные формы и контракты ответов API»?
Используйте Zod для вывода типов TypeScript из схем и валидации данных форм и ответов API. Ты практикуешь React Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.
Нужен ли мне опыт, чтобы начать React Academy?
Предыдущий опыт не требуется. React Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 4 из 4.
Сколько времени занимает урок «Типобезопасные формы и контракты ответов API»?
Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.
Можно ли писать и запускать код в этом уроке React Academy?
Да. Каждый урок React Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.
Все уроки этого курса
- Размеченные объединения для вариантов компонентов
- Условные и отображаемые типы в React
- Полиморфные компоненты со свойством 'as'
- Типобезопасные формы и контракты ответов API