🚐

Zod Mini 移行は怖くない

に公開

Zod v4 から Zod Mini という軽量板が同梱されるようになった。

https://zod.dev/packages/mini

Mini という名前がついているが、内部的には core をそのまま露出している。

https://github.com/colinhacks/zod/blob/main/packages/zod/src/v4/mini/external.ts

既存のメソッドチェインインターフェイスは classic という形でまとめられている。

https://github.com/colinhacks/zod/tree/main/packages/zod/src/v4/classic

これを鑑みると、次が予想できる。

  • Mini 側のインターフェイスが今後長く使われるだろうこと
  • classic はあるていど提供されるが、サポートされなくなる可能性が出てきたこと

というわけで、今後はじめるプロジェクトでは Zod Mini を使っていきましょう、ということになる。

移行しないとダメ?

で、既存プロジェクトで v3 を使っている場合など、移行をどうするか?という課題が浮上する。

一応、公式では Zod Mini への移行はまだ推奨されていない

https://zod.dev/packages/mini?id=when-not-to-use-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 ベースで出力してくるくらいだろうか。

GitHubで編集を提案

Discussion