0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Zodで安全にAPIレスポンスとフォームを検証する入門ガイド

0
Posted at

Web アプリでは外部 API から取得したデータやユーザーが入力した情報を、そのまま扱うとバグやセキュリティリスクにつながります。
そこで登場するのが「ランタイムバリデーション」です。TypeScript の型はコンパイル時にしか効きませんが、実行時にデータ構造をチェックできるライブラリとして Zod が人気です。この記事では、初心者でもすぐに使える Zod の基本と、APIレスポンス・フォーム入力の2つのシーンでの具体的な使い方を紹介します。

1. Zod の基本

Zod はスキーマを宣言し、parse メソッドでデータを検証します。検証に失敗すると例外が投げられ、エラーメッセージが取得できます。

import { z } from "zod";

// 文字列の name と数値の age を持つオブジェクト
const UserSchema = z.object({
  name: z.string(),
  age: z.number().int().nonnegative(),
});

// 正常データ
UserSchema.parse({ name: "Taro", age: 28 }); // → { name: "Taro", age: 28 }

// 異常データ(例外が発生)
try {
  UserSchema.parse({ name: "Hanako", age: -5 });
} catch (e) {
  console.error(e);
}

UserSchema からは TypeScript の型が自動的に推論され、User 型として再利用できます。

type User = z.infer<typeof UserSchema>;

2. APIレスポンスの検証

外部サービスから JSON を取得したら、まず Zod で形を確かめましょう。以下は GitHub API の一部レスポンスを検証する例です。

import { z } from "zod";

const RepoSchema = z.object({
  id: z.number(),
  name: z.string(),
  stargazers_count: z.number(),
  private: z.boolean(),
});

type Repo = z.infer<typeof RepoSchema>;

async function fetchRepo(owner: string, repo: string): Promise<Repo> {
  const res = await fetch(`https://api.github.com/repos/${owner}/${repo}`);
  const json = await res.json();
  // ここでランタイム検証
  return RepoSchema.parse(json);
}

parse が成功すれば、以降のコードでは Repo 型が保証されるので、IDE の補完や型チェックがフルに活きます。

3. フォーム入力の検証

次に、React でのフォームバリデーションを Zod に任せる方法です。react-hook-form と組み合わせるとシンプルに書けます。

import { useForm } from "react-hook-form";
import { z } from "zod";
import { zodResolver } from "@hookform/resolvers/zod";

const ContactSchema = z.object({
  email: z.string().email(),
  message: z.string().min(10, "10文字以上入力してください"),
});

type Contact = z.infer<typeof ContactSchema>;

export default function ContactForm() {
  const { register, handleSubmit, formState: { errors } } = useForm<Contact>({
    resolver: zodResolver(ContactSchema),
  });

  const onSubmit = (data: Contact) => {
    console.log("送信データ:", data);
  };

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <input {...register("email")} placeholder="メールアドレス" />
      {errors.email && <p>{errors.email.message}</p>}

      <textarea {...register("message")} placeholder="メッセージ" />
      {errors.message && <p>{errors.message.message}</p>}

      <button type="submit">送信</button>
    </form>
  );
}

zodResolver が Zod スキーマを react-hook-form のバリデーションロジックに変換し、エラーメッセージを自動で取得できます。

4. スキーマを共有してコードを減らす

API から取得したデータをそのまま編集画面に流すケースでは、同じスキーマを API とフォームの両方で使えるのが大きなメリットです。

// 共通スキーマ
const ArticleSchema = z.object({
  title: z.string().min(1),
  body: z.string().min(10),
});

// API 取得
async function getArticle(id: number) {
  const json = await (await fetch(`/api/articles/${id}`)).json();
  return ArticleSchema.parse(json); // 型安全に取得
}

// フォーム
type Article = z.infer<typeof ArticleSchema>;
const { register, handleSubmit } = useForm<Article>({
  resolver: zodResolver(ArticleSchema),
});

このようにスキーマを一元管理すれば、型の食い違いや重複したバリデーションロジックを防げます。

まとめ

  • Zod はシンプルな宣言でランタイムバリデーションを実現
  • parse で例外が出たらすぐに問題を検知でき、型推論で開発効率が向上
  • API とフォームの両方で同じスキーマを使うと保守性が高まる

実際に手を動かしてみると、バグの早期発見が格段に楽になります。ぜひプロジェクトに取り入れてみてください。

詳しい手順はこちら → https://felixstudio0.gumroad.com/?utm_source=qiita&utm_medium=github_actions&utm_campaign=2026-q1&utm_content=qiita-footer

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?