Zodを活用した認証フローとAPIレスポンス設計の実践例を紹介

Zodを導入する恩恵の一つとして、ユーザー入力やAPIとのやり取りにおいて型とデータ整合性を保証できることがあります。
今回は、ログイン・登録・認証チェックといった典型的なフローにZodをどう活用するか、API側とフロントエンド側での活用例を交えてわかりやすく解説していきます。
認証リクエストのバリデーション(ログイン編)
まずはログインフォームの入力バリデーションから。
バックエンドのリクエスト検証とフロントの型定義を共通化します。
共通スキーマ定義(shared-schema/auth.ts)
tsimport { z } from "zod";
export const loginSchema = z.object({
email: z.string().email(),
password: z.string().min(8),
});
export type LoginRequest = z.infer<typeof loginSchema>;
バックエンドでの検証(NestJSの例)
tsimport { loginSchema } from "@shared-schema/auth";
const body = await request.json();
const result = loginSchema.safeParse(body);
if (!result.success) {
return Response.json(
{ error: result.error.format() },
{ status: 400 }
);
}
// 認証処理へ
const { email, password } = result.data;
フロントエンドでのフォーム型として使用(React Hook Form)
tsimport { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import { loginSchema, LoginRequest } from "@shared-schema/auth";
const { register, handleSubmit, formState } = useForm<LoginRequest>({
resolver: zodResolver(loginSchema),
});
ユーザー登録(Sign Up)のバリデーションと整合性チェック
ユーザー登録では、フィールド間の整合性が求められます。superRefine
で実装可能です。
スキーマ定義(パスワード確認付き)
tsexport const signUpSchema = z
.object({
email: z.string().email(),
password: z.string().min(8),
confirmPassword: z.string(),
})
.superRefine(({ password, confirmPassword }, ctx) => {
if (password !== confirmPassword) {
ctx.addIssue({
path: ["confirmPassword"],
code: "custom",
message: "パスワードが一致しません",
});
}
});
export type SignUpRequest = z.infer<typeof signUpSchema>;
APIレスポンスの型安全化と整合性チェック
APIレスポンスの構造にもZodを活用できます。
これにより、サーバーから受け取ったデータの型安全性と妥当性を確保できます。
成功・エラー応答のUnion定義
tsconst successResponseSchema = z.object({
status: z.literal("ok"),
user: z.object({
id: z.string().uuid(),
name: z.string(),
email: z.string().email(),
}),
});
const errorResponseSchema = z.object({
status: z.literal("error"),
message: z.string(),
});
export const loginResponseSchema = z.union([
successResponseSchema,
errorResponseSchema,
]);
export type LoginResponse = z.infer<typeof loginResponseSchema>;
フロントエンドでのレスポンスチェック
tsconst res = await fetch("/api/login", {
method: "POST",
body: JSON.stringify(values),
});
const json = await res.json();
const result = loginResponseSchema.safeParse(json);
if (!result.success) {
throw new Error("レスポンス構造が不正です");
}
if (result.data.status === "ok") {
console.log("ようこそ", result.data.user.name);
} else {
alert("ログインエラー: " + result.data.message);
}
JWT付きセッションチェックなどの発展パターン
APIの認証チェックでも、Zodは活躍します。
例えばJWTのペイロード検証や、APIレスポンスが未ログイン・ログイン済で分かれる場合などに便利です。
JWTペイロード検証
tsconst jwtPayloadSchema = z.object({
sub: z.string().uuid(), // ユーザーID
exp: z.number(), // 有効期限
iat: z.number(), // 発行時刻
});
const decoded = jwt.verify(token, secret);
const result = jwtPayloadSchema.safeParse(decoded);
if (!result.success) {
throw new Error("トークンが無効です");
}
サーバーレスポンスの設計パターン一覧
ケース | スキーマ設計例 |
---|---|
ログイン成功 | status + user |
ログイン失敗 | status + message |
セッション期限切れ | status: "unauthorized" + message |
フィールドエラー | status: "validation_error" + errors[] |
任意フィールドのnull許容 | z.string().nullable() で対応 |
まとめ
Zodを用いることで、認証フローやAPI通信において次のような恩恵が得られます。
メリット | 内容 |
---|---|
型とバリデーションの一元管理 | リクエスト・レスポンス・フォームすべてで型を共有 |
実行時安全性の向上 | サーバー側でのパースエラーや構造違反を早期検出 |
変更耐性のある設計 | スキーマの中心化により変更がプロジェクト全体に自動反映 |
型の補完と検証の自動化 | IDE補完と安全なデータ操作が保証され、開発効率が向上 |
実運用に即した設計こそ、Zodの真価が発揮される場面です。
認証・認可・セッション管理といったセンシティブな領域にも、安心して導入できる強力なツールなためぜひ活用してみて下さい。
記事Article
もっと見る- article
NestJSでバリデーションエラーをログ出力する設定を紹介
- article
NestJSで作成したAPIのレスポンスヘッダーに付与されるx-powered-by: Express を消す方法を紹介
- article
Next.jsで環境変数に別の変数を利用し柔軟に管理するdotenv-expandの活用法を紹介
- article
【2025年3月版】Cursor ProとAPI利用比較。 Claude・GPT-4o・o1・GPT-4.5の損益分岐点と選び方
- article
フォーム入力情報からZodを利用してDTO作成しへ変換処理を実施するやり方を紹介
- article
Zodバリデーションのエラーメッセージを日本語化すやり方を紹介