はじめに
Next.jsのチューリアル第12章をやっているときに、Zod
というライブラリを使っていました。
何のことか分からなかったので調べることにしました。
Zodとは
TypeScript
における型の検証を簡単にしてくれるライブラリです。
以下のコードはNext.jsチュートリアルの第12章で実際に記述されているコードです。
この記事ではこのコードをもとにZod
を学びます。
'use server';
import { z } from 'zod';
const FormSchema = z.object({
id: z.string(),
customerId: z.string(),
amount: z.coerce.number(),
status: z.enum(['pending', 'paid']),
date: z.string(),
});
const CreateInvoice = FormSchema.omit({ id: true, date: true });
export async function createInvoice(formData: FormData) {
const rawFormData = CreateInvoice.parse({
customerId: formData.get('customerId'),
amount: formData.get('amount'),
status: formData.get('status'),
});
}
使用するコードの説明
上のコードの意味が分かってないと頭に入ってこないと思うので最初にそこを説明します。
createInvoice
という関数は新しいデータ(請求書)を作成しようとしている関数です。
このコードでは、フォームから受け取った値をrawFormData
に格納しているだけです。
実際のフォームが以下の画像です。
<form />
タグ内には以下の要素があります。
- nameに
customerId
を持った<select />
タグ - nameに
amout
を持った<input />
タグ - nameに
status
を持った<input />
タグ
以下コンソールにrawFormData
を出力した場合の例
{
customerId: '13d07535-c59e-4157-a011-f8d2ef4e0cbb',
amount: 234,
status: 'pending'
}
Zodを使って型検証をしよう
それではZod
の説明を始めましょう。
まずインストールですね。
npm install zod # npm
yarn add zod # yarn
bun add zod # bun
pnpm add zod # pnpm
そして、見やすいようにもう一度使用するコードを貼っておきます。
'use server';
import { z } from 'zod';
const FormSchema = z.object({
id: z.string(),
customerId: z.string(),
amount: z.coerce.number(),
status: z.enum(['pending', 'paid']),
date: z.string(),
});
const CreateInvoice = FormSchema.omit({ id: true, date: true });
export async function createInvoice(formData: FormData) {
const rawFormData = CreateInvoice.parse({
customerId: formData.get('customerId'),
amount: formData.get('amount'),
status: formData.get('status'),
});
}
import
{ z }
としてimport
するみたいですね。
1文字だけでimport
するという仕方で違和感を覚えました。
import { z } from 'zod';
スキーマの宣言
例えばstring
型のスキーマを宣言したい場合は、
const TestSchema = z.string()
とするみたいですね。
object
今回は受け取ったフォームの情報をrawFormData
というオブジェクトに変換するのでz.object
を付けます。
const FormSchema = z.object({});
string
id
とcustomerId
とdate
はstring
として扱うのでz.string()
を付けます。
const FormSchema = z.object({
+ id: z.string(),
+ customerId: z.string(),
+ date: z.string(),
});
number
amount
はnumber
として扱うのでz.number()
を付けます。
気になっているであろうcoerce
は下で解説します。
const FormSchema = z.object({
id: z.string(),
customerId: z.string(),
+ amount: z.coerce.number(),
date: z.string(),
});
coerce
coerce
ではデータの変換をすることができます。
フォームから送信されるデータは、基本的にはすべて文字列として扱われます。
ですが、amount
はnumber
として扱いたいので、coerce.number
を付けて変換しているというわけですね。
const FormSchema = z.object({
id: z.string(),
customerId: z.string(),
+ amount: z.coerce.number(),
date: z.string(),
});
enum
status
はpending
またはpaid
という文字列のみ許可するようにしたいです。
その時には、z.enum
を使うことで、列挙した文字列のうちいずれかの値であれば許可をすることができます。
Typescript
でいう"foo" | "baz"
みたいな感じですね。
const FormSchema = z.object({
id: z.string(),
customerId: z.string(),
amount: z.coerce.number(),
+ status: z.enum(['pending', 'paid']),
date: z.string(),
});
omit
omit
を使用すると宣言したスキーマから一部を削除することができます。
この場合、FormSchema
のid
とdate
は必要ないので、それぞれをtrue
とすることで削除ができるようです。
const CreateInvoice = FormSchema.omit({ id: true, date: true });
スキーマを使って検証 parse
完成したスキーマCreateInvoice
を使って検証しましょう。
スキーマ.parse(検証したい値)
とすることで検証ができます。
export async function createInvoice(formData: FormData) {
const rawFormData = CreateInvoice.parse({ // parse
customerId: formData.get('customerId'),
amount: formData.get('amount'),
status: formData.get('status'),
});
}
検証がtrue
だった場合は問題なくrawFormData
にオブジェクトが代入されます。
検証がfalse
だった場合はエラーになります。
おわりに
おそらくエラーハンドリングもするべきなんでしょうけど、Nextチュートリアルの第13章で触れるみたいなのでその後追記します。
参考