Zod Mini 移行は怖くない
Zod v4 から Zod Mini という軽量板が同梱されるようになった。
Mini という名前がついているが、内部的には core をそのまま露出している。
既存のメソッドチェインインターフェイスは classic という形でまとめられている。
これを鑑みると、次が予想できる。
- Mini 側のインターフェイスが今後長く使われるだろうこと
- classic はあるていど提供されるが、サポートされなくなる可能性が出てきたこと
というわけで、今後はじめるプロジェクトでは Zod Mini を使っていきましょう、ということになる。
移行しないとダメ?
で、既存プロジェクトで v3 を使っている場合など、移行をどうするか?という課題が浮上する。
一応、公式では Zod Mini への移行はまだ推奨されていない。
が、パフォーマンス面のみからの言及なので、明確に推奨しないというより、義務からの移行はしなくて良いというメッセージだと思われる。
また、今後の方向性はまだ決まっていないというか、議論が見つからなかったので、今後インターフェイスがどうなるかは不確定性が高い。
まだ Mini を継続サポートするかについても決まっていないと見た方が良い。多分軽量インターフェイスのニーズは公式もわかっていて、まずはこれでいけそうか、という市場調査の段階だと思われる。ただまあ、もう利用可能な形で出されているので、いざやめますとなっても、 breaking change 込みであるていどはサポートされるだろう。
重要となる、移行できるプロジェクトかどうかの見極めだが、 Mini では既存ユースケースのかなりの部分、多分 95% ていどはカバーしているので大体のプロジェクトで移行はできるはずだ。内部構造を前提としたハックをしていない限り大丈夫なはず。
総合すると、現段階では移行は義務に感じるものではなく、カリカリにパフォーマンスをチューニングしたい場合や、気になったらやってみるというのが良いだろう。
移行の際の課題
エラーハンドリングは内部インターフェイスである core
を使ってやらないといけないようだ。ライブラリー作成時は core
を使ってくれという文言は見かけたが、アプリケーションでこう使っているのが公式で意図しているかは不明。
import * as z from "zod/mini";
export function zodErrorToApiError(error: z.core.$ZodError): ApiError {
const flattened = z.flattenError(error);
// fieldErrors にはすべてのフィールドが含まれる
// エラーがない場合でも値が undefined として返ってくるため、 filter で除去する
const cleanFieldErrors = Object.entries(flattened.fieldErrors)
.filter((entry): entry is [string, string[]] => entry[1] !== undefined)
.reduce<Record<string, string[]>>(
(acc, [key, errors]) => ({
...acc,
[key]: errors,
}),
{}
);
return {
validationError: {
formErrors: flattened.formErrors,
fieldErrors: cleanFieldErrors,
},
};
}
他は公式が対照表を用意してくれているのもあって、すんなりと書き換えできた。個人的にはデータ型とバリデーションルールが分けられるので読みやすくなったなと感じている。
z.string().optional();
// 👇
z.optional(z.string());
z.number().int().min(0, "required");
// 👇
z.number().check(z.int(), z.minimum(0, "required"));
一応まだ v3 のコードベースが多いので code agent が v3 ベースで出力してくるくらいだろうか。
Discussion