SvelteKitのフォームバリデーションにZodを導入した話
フォームのバリデーションをif文で手書きすると項目が増えるたびにコードが複雑になります。Zodを使うとスキーマで宣言的に書けます。
インストール
npm install zod
Zodなしのバリデーション(if文が増えていく)
const title = data.get('title') as string;
const content = data.get('content') as string;
if (!title) return fail(400, { error: 'タイトルは必須です' });
if (title.length > 100) return fail(400, { error: 'タイトルは100文字以内' });
if (!content) return fail(400, { error: '本文は必須です' });
if (content.length < 10) return fail(400, { error: '本文は10文字以上' });
// 項目が増えるたびにif文が増える...
Zodを使うとスキーマで宣言的に書ける
// src/routes/learn/+page.server.ts
import { fail } from '@sveltejs/kit';
import { z } from 'zod';
import type { Actions } from './$types';
const TextSchema = z.object({
text: z.string()
.min(1, 'テキストを入力してください')
.max(50, '50文字以内で入力してください'),
});
export const actions: Actions = {
default: async ({ request }) => {
const data = await request.formData();
const result = TextSchema.safeParse({
text: data.get('text'),
});
if (!result.success) {
const errors = z.flattenError(result.error).fieldErrors;
return fail(400, {
errors,
text: data.get('text') as string,
});
}
// result.data は型が保証されている
const { text } = result.data;
// DB に保存する処理...
return { success: true };
},
};
ポイント:z.flattenError()
Zod v4 から error.flatten() が非推奨になり、z.flattenError(error) に変わりました。返ってくる fieldErrors の構造は同じです。
// ❌ Zod v4 では非推奨
const errors = result.error.flatten().fieldErrors;
// ✅ Zod v4 以降はこちら
const errors = z.flattenError(result.error).fieldErrors;
エラーを画面に表示する
<!-- src/routes/learn/+page.svelte -->
<script lang="ts">
import { enhance } from "$app/forms";
import type { ActionData } from "./$types";
type Props = { form: ActionData };
let { form }: Props = $props();
let submitting = $state(false);
</script>
<form
method="POST"
use:enhance={() => {
submitting = true;
return async ({ update }) => {
await update();
submitting = false;
};
}}
>
<input name="text" value={form?.text ?? ""} placeholder="タスクを入力(50文字以内)" />
{#if form?.errors?.text}
<p style="color: red">{form.errors.text[0]}</p>
{/if}
<button disabled={submitting}>
{submitting ? "送信中…" : "追加"}
</button>
</form>
fieldErrors はフィールドごとにエラーメッセージの配列を返します。[0] で最初のエラーを表示します。
よく使うZodのメソッド
// 文字列
z.string().min(1) // 最小文字数
z.string().max(100) // 最大文字数
z.string().email() // メール形式
z.string().url() // URL形式
z.string().regex(/^\d+$/) // 正規表現
// 数値
z.number().min(0) // 最小値
z.number().max(100) // 最大値
z.number().int() // 整数のみ
// その他
z.boolean()
z.enum(['draft', 'published']) // 特定の値のみ
z.optional(z.string()) // 省略可能
まとめ
| if文での手書き | Zod | |
|---|---|---|
| バリデーションルールの場所 | actionsの中に散在 | スキーマに集約 |
| TypeScriptの型 | 手動でキャスト | result.data に自動で付く |
| エラーの管理 | 自前で構造を作る | fieldErrors で自動分類 |
| 項目が増えたとき | if文が増える | スキーマにフィールドを追加するだけ |