😨

Zodを勉強していくぞっど

2023/05/10に公開

みなさんこんにちは!アルダグラムでエンジニアをしている大木です。

今回は、Zodというスキーマ生成とバリデーションをいい感じにできるライブラリについて紹介したいと思います。

紹介と言いつつも、私自身はZodをあまり使ったことがないため勉強したメモ書きみたいになるかと思います。それでも良ければお付き合いください。

溢れる出オチ感

出オチ感溢れるタイトルですが、噂の「ChatGPT」に聞いてみるとお世辞を言ってくれました。ありがとうございます。

きっかけ

ここからは真面目にやっていこうかと思います。

弊社のサービスである「KANNA」はノンデスクワーカー向けの施工管理アプリです。サービス上で案件を作成し、様々な情報をカスタマイズして入力することができます。

その入力画面では React Hook FormZod が利用されているのですが、今回はとある新規機能の開発でこれらを利用することになったため事前に勉強することにしました。

色々試してみる

Zodを利用して色々動作させていこうかと思います。記述していく内容はこちらの CodeSandbox のリンクからお試しいただけます。

(ボタンを押すことで各ケースの実行内容がコンソールに表示されるように作られています。雑な作りでごめんなさい…)

https://codesandbox.io/p/sandbox/zod-form-dhzs9v

ケース1: シンプルな文字列のスキーマ定義とバリデーション

以下のようにimportをして、動かしていきます。(denoを利用する場合は少し異なりますのでご注意ください)

import { z } from "zod"

スキーマを定義するのは以下のようにするだけなようです。めちゃめちゃシンプルですね。
特に指定をしない限りは、各スキーマのプロパティは必須プロパティになるようです。

const case1Schema = z.string();

定義したスキーマで文字列をvalidateしてみます。その際には、定義したスキーマで parse を実行することで可能なようです。

const result = case1Schema.parse("aaaaa")

// 'aaaaa'

問題なく値が取得できることがわかるかと思います。

ケース2: 文字列に制限を加えてみる

文字列でも、URLであることを確認するためのメソッドなんかも用意されています。他にも、メールアドレスや文字列の長さなどを確認するためのものも用意されています。
(公式ドキュメントでシンプルにわかりやすく説明されているので、詳しくは下記をご覧ください)

https://zod.dev/?id=strings

また、各メソッドの引数に message を与えることでvalidationに失敗した際のメッセージを定義することができるようです。

const case2Schema = z.string().url({ message: "Invalid URL!" });

const resultSuccess = case2Schema.safeParse("https://www.example.com")
if(resultSuccess.success) {
  console.log(resultSuccess.data)
} else {
  console.error(resultSuccess.error.errors)
}

// https://www.example.com

const resultFailure = case2Schema.safeParse("aaaaa")
if(resultFailure.success) {
  console.log(resultFailure.data)
} else {
  console.log("Invalid URL!")
}

// Invalid URL!

上記でvalidateする際には parse を利用すると記載しましたが、 safeParse というメソッドも利用できるようです。前者は例外を吐く可能性がありますが、後者は例外を吐かずに error にその内容が内包されて返却されるようです。
個人的には、基本的な方針として safeParse を利用する方が良いのかなと思ったりしました。(実際のところはどうなんでしょう)

ケース3: カスタムの定義を作成してみる

const case3Schema = z.object({
  name: z.string(),
  email: z.string().email({ message: "Invalid email!" }),
})

今回は、名前とメールアドレスを含んだスキーマを定義してみました。
これを使って

type Case3Inputs = z.infer<typeof case3Schema>

infer というメソッドを利用することで、定義したスキーマの型情報を取得することができます。
自分で type を定義する必要もなく、Zodに対する定義と2重で管理する必要もないので便利ですね。

ケース4: 入力された値を変換する

2つの正の数を入力できるようなスキーマを定義しました。

const case4Schema = z.object({
  number1: z.number().positive(),
  number2: z.number().positive()
})

各スキーマのプロパティを合成したい場合などに、 transform というメソッドが利用できます。

const result = case4Schema
  .transform(({ number1, number2 }, _) => number1 + number2)
  .parse({ number1: 1, number2: 2 })

console.log(result)
// 3

ケース5: Enumを定義する

Enumを定義することができます。

const case5Schema = z.enum(["red", "blue", "green"])

定義したスキーマの enum を利用することで定義した値を呼び出すことができそうです。
また、 options を利用することで一覧で取得できます。

console.log(case5Schema.enum.green)
// 'green'

case5Schema.options.forEach((option) => {
  console.log(option);
});
// 'red'
// 'blue'
// 'green'

他にも

今回記述した内容以外にも arrayunion など様々な型のスキーマを利用することができるようです。
再掲になりますが、公式ドキュメントに色々なサンプルが用意されているのでぜひご覧ください。

https://zod.dev/?id=basic-usage

まとめ

短い内容でしたが、ここまでご覧いただきありがとうございました!
Zodを利用することで、かなりシンプルにスキーマ定義やバリデーションができるなぁと感じました。公式のドキュメントもシンプルなため、とっつきやすい印象を受けました。
今回記述した内容とは別に、React Hook Form × Zod でフォームを作成してみました。ロジックとUIの分離がうまくできることや、Zodで生成したスキーマを使ってテストが記述しやすいこともメリットなのかなと感じました。

フロントエンドの技術の成長するスピードは凄まじいなと常々感じています。今回勉強したZodもそのうち利用されなくなってくるのかなと思うとゾッとしますね。Zodだけに。

アルダグラム Tech Blog

Discussion