型安全なFormsとAPI Response Contracts
ZodでschemasからTypeScript typesを推論し、form dataとAPI responsesの両方を検証します。
「型安全なFormsとAPI Response Contracts」はCoddyKit上の無料React Academyレッスンです。 これはレッスン4/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはReact Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 React Academyコースには全4レッスンが含まれています。
型安全なデータ形状が重要な理由
フォームとAPIレスポンスは、信頼できないソースからデータがアプリケーションに入ってくる境界です。Zodを使うと、実行時にバリデーションでき、同時にTypeScriptの型も推論できるスキーマを定義できます。
Zodスキーマの基礎
Zodの流暢なAPIでスキーマを定義します。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' }Zod Resolverを使ったReact Hook Form
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レスポンスをバリデーションする
ZodでAPIレスポンスをパースし、境界で形状の不一致を検出します。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
}安全なパースで適切にエラー処理する
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 を使った Server Action
データベースにアクセスする前に、Zod を使って Next.js の Server Action 内でフォームデータを検証します。
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 スキーマをエクスポートし、フロントエンドとバックエンドで同じ検証ロジックを使います。1つのスキーマを唯一の信頼できる情報源にします。
// 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 のエラーメッセージ
より良い UX のためにフィールドごとにエラーメッセージをカスタマイズします。Zod は resolver を介して、メッセージをフォームのエラー表示に自動的に渡します。
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'),
});確認問題
TypeScript で z.infer
まとめ
Zod スキーマを一度定義し、z.inferを使って TypeScript 型を派生させます。React Hook Form ではzodResolverを使って型安全な検証を行います。境界で Zod を使って API レスポンスを解析し、ユーザー向けのエラーにはsafeParseを使います。また、共有パッケージを介してフロントエンドとバックエンドでスキーマを共有します。
よくある質問
「型安全なFormsとAPI Response Contracts」レッスンは無料ですか?
はい。「型安全なFormsとAPI Response Contracts」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、React Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 React Academyコースには全4レッスンが含まれています。
「型安全なFormsとAPI Response Contracts」で何を学びますか?
ZodでschemasからTypeScript typesを推論し、form dataとAPI responsesの両方を検証します。 ブラウザで直接実行するハンズオンコードでReact Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。
React Academyを始めるのに経験は必要ですか?
事前経験は必要ありません。CoddyKitのReact Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン4/4です。
「型安全なFormsとAPI Response Contracts」レッスンにはどのくらい時間がかかりますか?
ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。
このReact Academyレッスンでコードを書いて実行できますか?
はい。すべてのReact Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。
このコースのすべてのレッスン
- Component VariantsのDiscriminated Unions
- ReactにおけるConditional TypesとMapped Types
- 'as' Propを使ったPolymorphic Components
- 型安全なFormsとAPI Response Contracts