Skip to Content
DocsVirtual Fields & Refinements

Virtual Fields & Refinements

Prisma Guard allows you to add virtual properties (fields that do not exist in your database but are required for request validation) and perform model-level schema checks (such as checking if password fields match).


Custom Virtual Fields (@zod.add)

Virtual fields are defined at the Model level using the /// @zod.add comment decorator. This is ideal for UI validations such as password confirmation fields or terms-of-service agreements.

Option 1: Inline Definitions

You can write raw Zod code inline directly inside your comment block:

/// @zod.add confirmPassword: z.string().min(8) /// @zod.add acceptTerms: z.boolean().refine(val => val === true, "You must accept the terms") model User { id Int @id @default(autoincrement()) email String @unique password String }

Option 2: Shorthand Preset Lookup (@zod.add.use)

If you have already defined a decorator pattern in your prisma-guard.config.js file, you can load it as a virtual field using the /// @zod.add.use(<decoratorName>) shorthand. The field name in the output schema will automatically match the decorator name.

/// @zod.add.use(confirmPassword) model User { id Int @id @default(autoincrement()) password String }

Model-Level Refinements (@zod.refine / /// @zod.create.refine)

You can apply Zod refinements or checks onto the entire model object to cross-validate multiple fields.

Refinement Scope Reference

DecoratorApplied To
/// @zod.refine(...)Both Create & Update schemas.
/// @zod.create.refine(...)Only the main Create schema.
/// @zod.update.refine(...)Only the Partial Update schema.
/// @zod.use(name)Shared preset decorator at model level.
/// @zod.create.use(name)Create-only preset decorator at model level.
/// @zod.update.use(name)Update-only preset decorator at model level.

Real-World Example: Password Matching

Here is how to combine @zod.add virtual fields and model-level refinements to enforce matching password inputs during registration:

Prisma Schema:

/// @zod.add confirmPassword: z.string() /// @zod.create.refine(data => data.password === data.confirmPassword, { /// @zod message: "Passwords do not match", /// @zod path: ["confirmPassword"] /// @zod }) model Account { id String @id @default(uuid()) email String @unique password String }

Generated Zod Output:

// generated/zod/account.ts import { z } from "zod"; import { REQUIRED_MESSAGE } from "../lib/constants"; export const AccountCreateSchema = z .object({ id: z.string().trim(), email: z.string().trim().min(1, { message: REQUIRED_MESSAGE }), password: z.string().trim().min(1, { message: REQUIRED_MESSAGE }), confirmPassword: z.string(), }) .refine( (data) => data.password === data.confirmPassword, { message: "Passwords do not match", path: ["confirmPassword"], } ); export const AccountUpdateSchema = AccountCreateSchema.partial();
Last updated on