Kento
zodスキーマ定義
2026年08月12日
見出しはありません
要約を生成中...
はじめてのzodを使ってsingUpの実装をしてみたので、メモ用に残そうと思って書いています!
使用技術
React
Next.js
TypeScript
react-hook-form
Auth.js
Prisma
PostgreSql
TypeScript向けの実行時のデータの検証(バリデーション)をやコンパイル時のデータの型(TypeScriptの型)を一つのスキーマ定義ができる便利なライブラリ。
npm install zod
以下は、signupのinputによるスキーマ定義です。 シンプルなオブジェクトスキーマを使用します。 このように、まずはスキーマを定義する必要があります。
スキーマは単純なプリミティブ型から複雑なネストされたオブジェクトや配列まで 様々な形を表すことができます。
プリミティブ型とは、booleanとかstring,number型などデータ型の種類のこと。
import z from "zod";
// サインアップようのバリデーションの型定義
export const signUpSchema = z.object({
userName: z
.string()
.min(1, "ユーザー名は必須入力です")
.max(20, "ユーザー名は20文字以内で入力してください"),
email: z.email("正しいメールアドレスを入力してください"),
password: z
.string()
.min(1, "パスワードは必須入力です")
.min(8, "パスワードは8文字以上で入力してください"),
});
export type SignUpInput = z.infer<typeof signUpSchema>;
入力するデータに適切なデータ型を設定。 emailを検証するならstringではなくemailを指定する。
userName: z.string()
email: z.email()
password: z.string()
文字数の制限は、シンプルにmaxやminを活用する。 検証失敗した時の、エラーメッセージを記述もできる。
.string()
.min(1, "ユーザー名は必須入力です")
.max(20, "ユーザー名は20文字以内で入力してください"),
password: z
.string()
.min(1, "パスワードは必須入力です")
.min(8, "パスワードは8文字以上で入力してください"),
zodには、エラーの優先順位があるので以下の公式を参照してください。
https://zod.dev/error-customization#error-precedence
今回、signUpフォームのデータをPOST送信してデータベースに保存することをやりたいので、サーバー側ではNext.jsのServer Actionsで実装。 ServerActionsだと、別途APIの実装(featchなど)しなくて済むのとクライアントとサーバーのやり取りを削減できるので、記述量を減らせます。
zodの使い方のみで、その他の説明は省きます!
"use server";
import z from "zod";
import { signUpSchema } from "@/features/auth/schemas/signUpSchema";
import { getUserByEmail } from "../repositories/user";
import { prisma } from "@/utils/prisma";
import { ActionsResult } from "./actionsResult";
import bcrypt from "bcrypt";
// signup全体の処理の流れ
export const signUp = async (
values: z.infer<typeof signUpSchema>,
): Promise<ActionsResult> => {
const validatedFields = signUpSchema.safeParse(values);
if (!validatedFields.success) {
return {
isSuccess: false,
error: {
message: validatedFields.error.message,
},
};
}
const { email, password, userName } = validatedFields.data;
try {
const hashedPassword = await bcrypt.hash(password, 10);
const existingUser = await getUserByEmail(email);
if (existingUser) {
return {
isSuccess: false,
error: {
message: "このメールアドレスは既に登録されています。",
},
};
}
await prisma.user.create({
data: {
userName: userName,
email: email,
password_hash: hashedPassword,
},
});
return {
isSuccess: true,
message: "サインアップに成功しました。",
};
} catch (error) {
console.error(error);
return {
isSuccess: false,
error: {
message: "サインアップに失敗しました。",
},
};
}
};
zodには、「画面から入ってくる入力値(input)」を検証して「安全が保障された出力値(output)」に変換してくれます。この流れの中で、以下の2つが重要な役割を担っています。
z.inferの役割
スキーマから型を「出力(抽出)」する役割。(TypeScriptの型定義を作る)
関数の引数にvaluesに型(signUpSchema)をあてることで、スキーマの「型」として使い回すことができるため、手動で型を二重管理する手間を省けて、スキーマと型の不一致を防げます。
safeParseの役割
データが正しいか「検証(バリデーション)」をする役割(Javascriptを実行してチェックをする)
スキーマで定義した検証内容をもとに、文字数や不正なデータなどをチェックし、サーバー手前で遮断する。
export const signUp = async (
values: z.infer<typeof signUpSchema>,
): Promise<ActionsResult> => {
const validatedFields = signUpSchema.safeParse(values);
入力された生データをsafeParseで検問し、問題ないデータをz.inferで作った型として出力する処理の流れになります。
サーバー側で、データ検証をもとにDBへデータに保存(POST送信)をしました。 次は、UX向上のため、スキーマ定義をもとにフロント側にバリデーションを表示させる実装をします。
"use client";
import { useForm } from "react-hook-form";
import { signUp } from "./actions/signup";
import { SignUpInput } from "./schemas/signUpSchema";
import { signUpSchema } from "./schemas/signUpSchema";
import { AuthFormModeProps } from "@/types/auth";
import { signUpFields } from "@/config/auth/authFields";
import { FormInput } from "@/components/input/formInput";
import { zodResolver } from "@hookform/resolvers/zod";
export const SignUpForm = ({ mode }: AuthFormModeProps) => {
const {
register,
handleSubmit,
formState: { errors },
} = useForm<SignUpInput>({resolver: zodResolver(signUpSchema)});
const onSubmit = async (data: SignUpInput) => {
const result = await signUp(data);
if (!result.isSuccess) {
return result.error.message;
}
};
return (
<form
onSubmit={handleSubmit(onSubmit)}
className="flex flex-col gap-4 bg-[#333333] p-[30px] rounded-xl"
>
{signUpFields.map((field) => (
<FormInput
key={field.name}
label={field.label}
type={field.type}
registration={register(field.name)}
error={errors[field.name]?.message}
/>
))}
<button
type="submit"
className="border-none rounded-md text-black bg-white p-3 mt-[50px] mx-[120px] hover:bg-gray-300"
>
{mode === "signup" ? "新規登録" : "ログイン"}
</button>
</form>
);
};
今回はreact-hook-formも活用して実装をしています。 zodは、フォームの値を検証する機能が備わっています。zodResolverを設定します。
zodResolverの役割
zodResolverとは、zodで定義したバリデーションルールをreact-hook-formに適用する関数。
型をチェックし、バリデーションの結果をエラーメッセージとして提供をしてくれる。
import { SignUpInput } from "./schemas/signUpSchema";
import { signUpSchema } from "./schemas/signUpSchema";
export const SignUpForm = ({ mode }: AuthFormModeProps) => {
const {
register,
handleSubmit,
formState: { errors },
} = useForm<SignUpInput>({resolver: zodResolver(signUpSchema)});
今回は、signUpでzodで一つのスキーマ定義を使って、型のチェックやバリデーションを実装してみました。 紹介した内容は一部に過ぎず、いろんな使い方やカスタムもできますのでとても便利なライブラリだと思いました!
要約
コメント
まだコメントはありません。