はじめに
Zodの公式ドキュメントを眺めていると、
「あ、ここまで標準でサポートしてくれるなら積極的に使っていきたいな」
と思う便利どころがいろいろ揃っています。
この記事は、Zodの公式ドキュメントの該当箇所を引用・整理しつつ、実際に読んでいて私が「これ便利!」と感じたところだけをピックアップしてまとめたものです。
取り上げるのは、主に次のあたりです。
- Primitives(基本の型)
- 文字列バリデーション / 変換(Strings)
- よくあるフォーマット(String formats / Emails)
- 特に刺さった stringbool と Custom formats(stringFormat)
公式ドキュメントの全部解説ではなく、
「ここだけ押さえておくと嬉しいかも」というポイントをざっと紹介します✨
Primitives(基本の型)
import * as z from "zod";
z.string();
z.number();
z.bigint();
z.boolean();
z.symbol();
z.undefined();
z.null();
型定義とスキーマ定義のギャップがほとんどなく、そのまま書けるのが Zod の好きなところだなぁと思います。
Strings(文字列バリデーション & Transform)
Zod は、文字列まわりでよく使う検証や変換がひと通り揃っています。
z.string().max(5);
z.string().min(5);
z.string().length(5);
z.string().regex(/^[a-z]+$/);
z.string().startsWith("aaa");
z.string().endsWith("zzz");
z.string().includes("---");
z.string().uppercase();
z.string().lowercase();
細かいルールを毎回正規表現で書かなくてよくなるのが、うれしいところです。
String formats(よくある文字列フォーマット)
正規表現を自前で書くより、Zod が用意しているフォーマットを素直に使ったほうが読みやすくなります。
z.email();
z.uuid();
z.url();
z.httpUrl(); // http or https URLs only
z.hostname();
z.emoji(); // validates a single emoji character
z.base64();
z.base64url();
z.hex();
z.jwt();
z.nanoid();
z.cuid();
z.cuid2();
z.ulid();
z.ipv4();
z.ipv6();
z.mac();
z.cidrv4(); // ipv4 CIDR block
z.cidrv6(); // ipv6 CIDR block
z.hash("sha256"); // or "sha1", "sha384", "sha512", "md5"
z.iso.date();
z.iso.time();
z.iso.datetime();
z.iso.duration();
ID 系(uuid / cuid / nanoid / ulid)、
ネットワーク系(ipv4 / ipv6 / mac / cidrv4 / cidrv6)、
ハッシュ値(hash)、日付・時間までカバーされていて、
「ここは Zod のフォーマット名で書いておけば意図がすぐ伝わる」
というのが特に良いポイントだなと感じました。
Emails(メールアドレス)
メールアドレスは z.email() 一発です。
z.email();
内部では、実運用でよく見るパターンを想定した正規表現が使われています。
/^(?!\.)(?!.*\.\.)([a-z0-9_'+\-\.]*)[a-z0-9_+-]@([a-z0-9][a-z0-9\-]*\.)+[a-z]{2,}$/i
Zodの公式ドキュメント によると、「一般的な文字を含む通常のメールアドレスを検証するための、比較的厳格な正規表現」で、Gmail が施行しているルールとほぼ同じレベル とのことでした。
特に「これ便利!」と思ったもの
ここからは、今回の中でも特に良いなと思ったものを 2 つだけピックアップします。
z.stringbool()(Zod 4に新登場)
文字列として渡ってくる "true" / "false" / "1" / "0" などを boolean に変換してくれるスキーマ です。
const flag = z.stringbool();
flag.parse("true"); // true
flag.parse("yes"); // true
flag.parse("0"); // false
flag.parse("off"); // false
- 環境変数
- クエリパラメータ
- フロント → バックエンドの string ベースのフラグ
みたいな、「本当は boolean が欲しいのに文字列で来る」ところと相性がいいです。
truthy / falsy の単語リストを自分で定義できるので、
プロジェクトごとのルールに合わせやすいのもポイントです。
Custom formats(stringFormat)
stringFormat を使うと、独自のフォーマット名を付けて string のルールを定義できます。
const coolId = z.stringFormat("cool-id", (val) => {
// 任意のバリデーションロジック
return val.length === 100 && val.startsWith("cool-");
});
z.stringFormat("cool-id", /^cool-[a-z0-9]{95}$/);
Zodの公式ドキュメントによると、stringFormat で定義したスキーマはrefine() や z.custom() とは異なり、"custom" ではなく "invalid_format" というエラーコードを出すのがポイントです。
coolId.parse("invalid input!");
// ZodError: [
// {
// code: "invalid_format",
// format: "cool-id",
// path: [],
// message: "Invalid cool-id",
// },
// ]
このおかげで、
- エラー種別が「invalid_format」で統一される
- どのフォーマットで落ちたかが「format: "cool-id"」として分かる
- スキーマを見たときに「これは cool-id というフォーマット」というのが一目で分かる
というメリットがあります。
アプリケーション固有の表現など、ドメイン固有フォーマットに名前を付けて管理できるのがとても便利だなと感じました💡
まとめ
Zod のスキーマ定義をあらためて見返してみると、
文字列バリデーションやフォーマットの「よくあるパターン」が網羅的に用意されていることがよく分かります。
特に今回は、
- stringbool
- stringFormat(Custom formats)
が、「これ便利!」と特に感じたところでした。
Zod は便利な機能が本当に多く揃っているので、これからも積極的に使っていきたいです☺️🌈