😊
Prisma開発者必見!zod-prisma-typesが生成する3つのスキーマの使い方
1. はじめに
- 本記事では、PrismaとZodを組み合わせたzod-prisma-typesを使用して、型安全なバリデーションを実装する方法を解説します。
- 特に、zod-prisma-typesが自動生成する3つのスキーマ(inputTypeSchemas、modelSchema、outputTypeSchemas)に焦点を当て、関連テーブルを含むデータのバリデーションと操作方法を詳しく説明します。
- 対象バージョンは
v3.1.8
対象読者
- Prismaの基本を理解している初級から中級レベルの開発者
- zod-prisma-typesの効果的な活用方法を学びたい方
2. zod-prisma-typesのセットアップと基本的な使用方法
本記事のディレクトリ構造
記事で言及するディレクトリとファイルは以下の通りです。
project-root/
├── prisma/
│ ├── schema.prisma
│ └── generated/
│ └── zod/
│ ├── inputTypeSchemas/
│ │ ├── UserCreateInputSchema.ts
│ │ └── PostCreateInputSchema.ts
│ ├── modelSchema/
│ │ ├── UserSchema.ts
│ │ └── PostSchema.ts
│ └── outputTypeSchemas/
│ ├── UserArgsSchema.ts
│ └── UserCreateArgsSchema.ts
-
prisma/schema.prisma
: Prismaのスキーマ定義ファイル -
prisma/generated/zod/
: zod-prisma-typesによって生成されるZodスキーマのディレクトリ-
inputTypeSchemas/
: データベース入力操作(作成、更新)するためのスキーマ -
modelSchema/
: データベースモデル全体の構造を表現するためのスキーマ -
outputTypeSchemas/
: データベースからのクエリ結果の形状を定義するスためのキーマ
-
2.1 プロジェクトのセットアップ
- 必要なパッケージのインストール
npm install zod zod-prisma-types @prisma/client
-
prisma/schema.prisma
ファイルに以下の generator を追加
generator client {
provider = "prisma-client-js"
}
generator zod {
provider = "zod-prisma-types"
useMultipleFiles = true
writeBarrelFiles = false
useTypeAssertions = true
}
この設定で、Prisma スキーマから自動的に Zod スキーマを生成します。
- スキーマ生成コマンドの実行
npx prisma generate
2.2 Prismaスキーマの定義
ユーザー(User)と投稿(Post)の関連を持つブログを例に、Prismaスキーマを定義します。
model User {
id Int @id @default(autoincrement())
email String @unique
name String?
posts Post[]
}
model Post {
id Int @id @default(autoincrement())
title String
content String?
published Boolean @default(false)
author User @relation(fields: [authorId], references: [id])
authorId Int
}
このスキーマの主なポイントは以下の通りです。
-
User
モデルとPost
モデルを定義 -
User
とPost
一対多の関係 -
Post
は必ず一人のUser
(著者)に属する
2.3 生成されたスキーマの使用
zod-prisma-typesが生成したZodスキーマは@/prisma/generated/zod
に保存されます。
これらのスキーマを使用してデータのバリデーションを行います。ここでは、omit
メソッドとsafeParse
メソッドを使用した例を示します。
import { z } from 'zod';
import { UserSchema, PostSchema } from '@/prisma/generated/zod/modelSchema';
// omitメソッドを使用して入力用のスキーマを定義
// omit: 指定したフィールドを除外した新しいスキーマを作成する
// これにより、自動生成されるidやデータベース側で管理される関連フィールドを除外できる
// UserSchemaからidフィールドを除外した入力用スキーマを作成
const UserInputSchema = UserSchema.omit({ id: true });
// PostSchemaからidとauthorIdフィールドを除外した入力用スキーマを作成
// authorIdは関連テーブルの操作時に別途処理するため、ここでは除外する
const PostInputSchema = PostSchema.omit({ id: true, authorId: true });
// ユーザー作成データのバリデーション例
const validUserInput = {
email: "user@example.com",
name: "John Doe"
};
const invalidUserInput = {
email: "invalid-email",
name: 123 // 不正な型
};
// 投稿作成データのバリデーション例
const validPostInput = {
title: "新しい投稿",
content: "これは新しい投稿の内容です。",
published: true
};
const invalidPostInput = {
title: "", // 空のタイトル
content: "内容",
published: "yes" // 不正な型
};
// バリデーション実行
const validUserResult = UserInputSchema.safeParse(validUserInput);
const invalidUserResult = UserInputSchema.safeParse(invalidUserInput);
const validPostResult = PostInputSchema.safeParse(validPostInput);
const invalidPostResult = PostInputSchema.safeParse(invalidPostInput);
-
omit
メソッド- 不要なフィールドを除外した新しいスキーマの作成できるメソッド
-
UserSchema.omit({ id: true })
-
id
フィールドを除外した新しいスキーマを作成
-
-
PostSchema.omit({ id: true, authorId: true })
-
id
とauthorId
フィールドを除外
-
-
- 不要なフィールドを除外した新しいスキーマの作成できるメソッド
-
safeParse
メソッド- バリデーション実行と結果オブジェクトの返却するメソッド
- 成功時は
{ success: true, data: validatedData }
を返却 - 失敗時は
{ success: false, error: ZodError }
を返却
- 成功時は
- バリデーション実行と結果オブジェクトの返却するメソッド
バリデーションが成功した場合の処理
if (validUserResult.success) {
console.log("有効なユーザーデータ:", validUserResult.data);
// ログ出力: 有効なユーザーデータ: { email: "user@example.com", name: "John Doe" }
// validUserResult.data は型安全
const email: string = validUserResult.data.email;
const name: string | undefined = validUserResult.data.name;
// このデータを使用してデータベース操作などを行う
// 例: await prisma.user.create({ data: validUserResult.data });
}
if (validPostResult.success) {
console.log("有効な投稿データ:", validPostResult.data);
// ログ出力: 有効な投稿データ: { title: "新しい投稿", content: "これは新しい投稿の内容です。", published: true }
// validPostResult.data は型安全
const title: string = validPostResult.data.title;
const content: string | undefined = validPostResult.data.content;
const published: boolean = validPostResult.data.published;
}
バリデーションエラーの処理
if (!invalidUserResult.success) {
console.error("ユーザーデータのバリデーションエラー:");
invalidUserResult.error.issues.forEach(issue => {
console.error(`- ${issue.path.join('.')}: ${issue.message}`);
});
// ログ出力:
// ユーザーデータのバリデーションエラー:
// - email: Invalid email
// - name: Expected string, received number
}
if (!invalidPostResult.success) {
console.error("投稿データのバリデーションエラー:");
invalidPostResult.error.issues.forEach(issue => {
console.error(`- ${issue.path.join('.')}: ${issue.message}`);
});
// ログ出力:
// 投稿データのバリデーションエラー:
// - title: String must contain at least 1 character(s)
// - published: Expected boolean, received string
}
-
error
プロパティにZodErrorオブジェクトを格納-
error.issues
配列に具体的なバリデーションエラーの詳細情報を格納- 各issueに
path
(エラーが発生したプロパティ)、message
(エラーメッセージ)、code
(エラーコード)を格納
- 各issueに
-
まとめ
-
omit
メソッド- クライアントサイドでの入力データの適切なバリデーションを実行
- 例:自動生成される
id
フィールドや関連フィールド(authorId
)を除外したスキーマを作成
-
safeParse
メソッド- バリデーション結果を
success
プロパティに格納し、成功と失敗を分類- 成功時(
success: true
):-
data
プロパティに型安全なバリデーション済みデータを格納 - このデータを直接使用可能
-
- 失敗時(
success: false
):-
error
プロパティに詳細なエラー情報を格納 -
error.issues
配列に具体的なバリデーションエラーの詳細情報を格納 - 各issueに
path
(エラーが発生したプロパティ)、message
(エラーメッセージ)、code
(エラーコード)などの情報を格納 - これらの情報を活用して入力フォームのエラーメッセージ表示や例外処理を実装
-
- 成功時(
- バリデーション結果を
3. zod-prisma-typesが生成する3つのスキーマ
- zod-prisma-typesは、
schema.prisma
から3つのZodスキーマを自動生成します。 - 各スキーマの特徴、役割、および使用方法について解説します。
注意
schema.prisma
の設定を以下のように設定してください。
generator zod {
provider = "zod-prisma-types"
useMultipleFiles = true
writeBarrelFiles = false
useTypeAssertions = true
}
3つのZodスキーマ
- inputTypeSchemas: データベース入力操作(作成、更新)するためのスキーマ
- modelSchema: データベースモデル全体の構造を表現するためのスキーマ
- outputTypeSchemas: データベースからのクエリ結果の形状を定義するスためのキーマ
3.1 inputTypeSchemas
prisma/generated/zod/inputTypeSchemas
に出力
概要と役割
- データベースへの入力操作(作成、更新)に使用
- データの作成(create)や更新(update)時のバリデーション
- リクエストボディの検証
- 必須フィールドと任意フィールドの指定
- 関連テーブルの操作(connect, create, update, upsert など)
使用例
import { PostCreateInputSchema, UserCreateInputSchema } from '@/prisma/generated/zod/inputTypeSchemas';
// 新しい投稿を作成し、既存のユーザーと関連付ける
const createPostWithExistingUser = PostCreateInputSchema.parse({
title: "新しい投稿",
content: "これは新しい投稿の内容です。",
author: {
connect: { id: existingUserId }
}
});
// 新しいユーザーを作成し、同時に新しい投稿も作成して関連付ける
const createUserWithNewPost = UserCreateInputSchema.parse({
name: "新しいユーザー",
email: "new@example.com",
posts: {
create: [
{
title: "新しいユーザーの最初の投稿",
content: "これは新しいユーザーが作成した最初の投稿です。"
}
]
}
});
-
PostCreateInputSchema.parse()
- 新しい投稿データをバリデーション
-
author.connect
で既存のユーザーとの関連付けを指定
-
UserCreateInputSchema.parse()
- 新しいユーザーデータをバリデーション
-
posts.create
で同時に新しい投稿を作成し関連付け
- 関連テーブルの操作
-
connect
- 既存のレコードの関連付けに使用
- 例:
author: { connect: { id: existingUserId } }
- この例では既存のユーザーを新しい投稿に関連付け
-
create
- 関連する新しいレコードの作成と同時に関連付けを実行
- 例:
posts: { create: [ { title: "新しい投稿", content: "これは新しい投稿の内容です。" } ] }
- この例では新しいユーザーを作成すると同時に、関連する投稿も作成
-
update
- 関連するレコードの更新
- 例:
posts: { update: { where: { id: postId }, data: { title: "更新されたタイトル" } } }
-
upsert
- レコードが存在しない場合は作成し、存在する場合は更新
- 例:
posts: { upsert: { where: { id: postId }, update: { title: "更新" }, create: { title: "新規作成" } } }
-
Tips
- これらの操作は、Prismaの機能を反映したZodスキーマによって型安全に実行可能
- スキーマは、Prismaで定義された関係性(一対多、多対多など)を正確に反映
3.2 modelSchema
prisma/generated/zod/modelSchema
に出力
概要と役割
- データベースモデル全体の構造を表現
- データベースから取得したデータの検証
- モデルの完全な構造の定義
- 全てのフィールド(主キー、外部キーを含む)の型チェック
使用例
import { UserSchema } from '@/prisma/generated/zod/modelSchema';
// データベースから取得したユーザーデータの検証
const user = await prisma.user.findUnique({ where: { id: userId } });
const validatedUser = UserSchema.parse(user);
console.log("検証済みユーザー:", validatedUser);
// 出力例: { id: 1, email: "user@example.com", name: "John Doe", posts: [...] }
// 型安全な操作
const userName: string | null = validatedUser.name;
const userEmail: string = validatedUser.email;
-
UserSchema.parse(user)
- データベースから取得したユーザーデータを検証
- 全てのフィールド(id, email, name, posts)が正しい型であることを確認
- 関連するpostsデータも含めた検証(取得している場合)
- 型安全な操作
-
validatedUser
オブジェクトを使用した型安全な操作が可能- TypeScriptの型チェックを活用し、潜在的なエラーを事前に防止
- 例:
const userName: string | null = validatedUser.name; const userEmail: string = validatedUser.email;
-
userName
はstring | null
型として推論され、名前が設定されていない可能性を考慮 -
userEmail
はstring
型として推論され、常に値が存在することを保証
-
3.3 outputTypeSchemas
prisma/generated/zod/outputTypeSchemas
に出力
概要と役割
- データベースからのクエリ結果の形状を定義
- クエリ引数の型定義と検証
- リレーションを含むクエリ結果の検証
- クライアントに返すデータ構造の定義
使用例
import { UserArgsSchema, UserCreateArgsSchema } from '@/prisma/generated/zod/outputTypeSchemas';
import { z } from 'zod';
// クエリ引数の型定義と検証
const userQuerySchema = UserArgsSchema.pick({
where: true,
select: true,
include: true
});
const userQueryInput = {
where: { id: 1 },
select: { id: true, name: true, email: true },
include: { posts: true }
};
const validatedUserQueryArgs = userQuerySchema.parse(userQueryInput);
// 上記の検証済み引数を使用してクエリを実行
const user = await prisma.user.findUnique(validatedUserQueryArgs);
// リレーションを含むクエリ結果の検証
const userWithPostsSchema = z.object({
id: z.number(),
name: z.string().nullable(),
email: z.string(),
posts: z.array(z.object({
id: z.number(),
title: z.string(),
content: z.string().nullable(),
published: z.boolean()
}))
});
const userWithPosts = await prisma.user.findUnique({
where: { id: 1 },
include: { posts: true }
});
const validatedUserWithPosts = userWithPostsSchema.parse(userWithPosts);
// ユーザー作成時の引数の検証
const createUserInput = {
data: {
name: "新規ユーザー",
email: "new@example.com",
posts: {
create: [
{ title: "最初の投稿", content: "これは新しいユーザーの最初の投稿です。" }
]
}
}
};
const validatedCreateUserArgs = UserCreateArgsSchema.parse(createUserInput);
// 検証済みの引数を使用してユーザーを作成
const newUser = await prisma.user.create(validatedCreateUserArgs);
console.log("新規ユーザー:", newUser);
// 出力例: { id: 2, name: "新規ユーザー", email: "new@example.com" }
説明
-
クエリ引数の型定義と検証
-
UserArgsSchema.pick()
を使用して、必要な引数のみを含むカスタムスキーマを作成 - このスキーマを使用して、
findUnique
などのクエリメソッドの引数を検証 -
where
、select
、include
オプションを含む引数構造の定義と検証
-
-
リレーションを含むクエリ結果の検証
-
z.object()
を使用して、ユーザーと関連する投稿を含むカスタムスキーマを定義 - このスキーマを使用して、データベースから取得したユーザーと投稿のデータを検証
- ネストされたデータ構造(ユーザーと関連する投稿)を正確に定義し、型安全性を確保
-
-
ユーザー作成時の引数の検証
-
UserCreateArgsSchema
を使用して、ユーザー作成時の完全な引数オブジェクトを検証 - ネストされたデータ構造(この場合は関連する
posts
の作成)も含めて検証 - 検証済みの引数を使用してデータベース操作を実行し、型安全性を維持
-
4. まとめ
この記事では、zod-prisma-typesを活用したバリデーションについて、特に、zod-prisma-typesが自動生成する3つのスキーマ(inputTypeSchemas、modelSchema、outputTypeSchemas)を解説しました。
主なポイントは以下の通りです。
-
zod-prisma-typesが生成する3つのスキーマの役割と使用方法
- inputTypeSchemas
- 役割:データベースへの入力操作(作成、更新)のバリデーション
- 使用方法:リクエストボディの検証、必須・任意フィールドの指定、関連テーブルの操作
- 例:
PostCreateInputSchema
,UserCreateInputSchema
の使用
- modelSchema
- 役割:データベースモデル全体の構造表現と検証
- 使用方法:データベースから取得したデータの完全性チェック、全フィールドの型チェック
- 例:
UserSchema
を使用したデータベースから取得したユーザーデータの検証
- outputTypeSchemas
- 役割:データベースクエリ結果の形状定義と検証
- 使用方法:クエリ引数の型定義、リレーションを含むクエリ結果の検証、クライアントに返すデータ構造の定義
- 例:
UserArgsSchema
,UserCreateArgsSchema
を使用したクエリ引数の検証
- inputTypeSchemas
-
zod-prisma-typesの主要メソッドのまとめ
-
omit
メソッド- 用途:特定のフィールドを除外した新しいスキーマの作成
- 例:
UserSchema.omit({ id: true })
でid
フィールドを除外
-
safeParse
メソッド- 用途:バリデーション結果の成功と失敗の判別、および適切な処理の実行
- 特徴:
- 成功時(
success: true
)-
data
プロパティに型安全なバリデーション済みデータを格納
-
- 失敗時(
success: false
)-
error
プロパティに詳細なエラー情報を格納-
error.issues
配列にpath
、message
、code
などの詳細情報を格納
-
-
- 成功時(
-
parse
メソッド- 用途:データの検証と型変換を行い、エラーがある場合は例外をスロー
- 例:
UserCreateArgsSchema.parse(createUserInput)
でユーザー作成時の引数を検証
- 関連テーブル操作メソッド
-
connect
:既存レコードの関連付け -
create
:新規レコードの作成と関連付け -
update
:関連レコードの更新 -
upsert
:レコードの作成または更新
-
-
pick
メソッド- 用途:特定のフィールドのみを選択して新しいスキーマを作成
- 例:
UserArgsSchema.pick({ where: true, select: true, include: true })
でクエリ引数のカスタムスキーマを作成
-
以上、最後までお読みいただきありがとうございました!
Discussion