Skip to main content

Comment Decorators

Prisma Guard allows you to annotate your Prisma schema files using triple-slash (///) comments to fine-tune validation constraints, define complex validation pipelines, or completely override field type definitions.


Inline Comment Decorators

You can add standard Zod validations inline directly next to your model fields.

Basic Usage

Use @zod.<validation> inline to append constraints to the inferred field:

model User {
id String @id
/// @zod.email()
email String
/// @zod.min(8).max(100)
name String
}

Multi-line Decorators

Prisma Guard supports multi-line comment blocks for complex logic, checks, and refinements. Start the block with a double-level decorator and prefix subsequent lines with /// @zod:

/// @zod.create.check(ctx => {
/// @zod if (ctx.value.role === 'ADMIN' && !ctx.value.secret) {
/// @zod ctx.issues.push({ code: 'custom', message: 'Admin needs a secret' });
/// @zod }
/// @zod })
model User {
id String @id
role String
/// @zod.optional()
secret String?
}

In the generated Zod schema, the lines starting with /// @zod are stitched together as clean, formatted JavaScript/TypeScript execution code.


Named Decorators (@zod.use)

For complex validations that are repeated across multiple models, you can define Named Decorators in your configuration. This keeps your .prisma schema readable and acts as a single source of truth for validation rules.

1. Define Decorators in Config

Define your validation pipelines inside the decorators property of prisma-guard.config.js. Use the v (validator) proxy helper:

import { defineConfig, v } from "@explita/prisma-guard";

export default defineConfig({
decorators: {
// Chain validations onto the inferred type
email: v.chain.email().trim().toLowerCase(),

// Absolute type override
strongPassword: v.string().min(12).regex(/[A-Z]/),

// Model-level validation check
ownerCheck: v.chain.check((ctx) => {
if (!ctx.value.userId) return false;
return true;
}),
},
});

2. Apply in your Prisma Schema

Reference the custom decorators using /// @zod.use(<decoratorName>):

/// @zod.use(ownerCheck)
model Project {
id String @id
/// @zod.use(email)
email String
}

Understanding the v Proxy API

Prisma Guard exports a specialized v (validator) proxy object (aliased to validator).

[!IMPORTANT] Why use v instead of Zod's z in your configuration?

  1. Proxy Serialization (Stringification): At configuration load time, v records your method calls and maps them to a string representation (e.g. v.string().min(8) becomes the string "z.string().min(8)"). This enables the compiler to generate clean, native Zod code in the final schema output. Standard z execution results in runtime objects that cannot be serialized back to typescript source code.
  2. Unified Imports: The v helper acts as a single interface. It handles custom type mappings (like relations or database scalar configurations) and auto-generates external import statements for variables/constants without requiring any manual imports.

Chaining vs. Absolute Overrides

MethodBehaviorUse CaseExample
v.chainAppends validations onto the default inferred Zod schema.Adding validation constraints (like bounds or formats) without overriding the core database type.v.chain.email().trim()
Direct v callReplaces the inferred schema definition completely.Changing types entirely, using complex schemas, or enforcing specific runtime types.v.string().min(10) or v.enum([...])
// ✅ Chain: Inferred default type (String -> z.string().trim()) is preserved, .email() is appended.
email: v.chain.email().trim()

// ✅ Override: Inferred default type is replaced with a custom enum definition.
role: v.enum(["ADMIN", "USER"])

// ❌ Warning: Chaining cannot be used to change the base type.
role: v.chain.enum(["ADMIN"]) // Will not work as expected!

v API Quick Reference

RequirementSyntaxOutput Representation
Chain validationsv.chain.email().trim().email().trim()
Replace type completelyv.string().min(8)z.string().min(8)
Reference constantsv.var("constants.MSG")Imports MSG from /lib/constants.ts
Reference with custom importv.var("(messages).err", "../../lib/msg")Imports messages from ../../lib/msg
Reference relation schemasv.array(v.relation("OrderItem"))Resolves to array of circular-safe OrderItem schemas
Complex nested typesv.record(v.string(), v.any())z.object({ ... }) type structures

Absolute Overrides in Comment Decorators

If you want to perform type overrides directly inside your .prisma schema comments rather than using a configuration decorator, use the absolute override prefix:

  • Use /// @zod.z. (e.g. /// @zod.z.email().min(5)) to override the inferred type completely inline.

[!NOTE] You must use an absolute override in comments for:

  1. Coercion: Changing parsing behavior, e.g. z.coerce.number().
  2. Base Type Changes: Turning a String field into a z.enum() or z.any().
  3. Complex Structures: Defining z.union(), z.record(), or z.lazy().

Sharing Decorators Across Projects

As your application grows, you can extract common decorators into a shared NPM or workspace package (e.g., @acme/validators) to keep validations consistent across microservices.

1. Define and Export Shared Decorators

// @acme/validators
import { v } from "@explita/prisma-guard";

export const companyDecorators = {
email: v.chain.email().trim().toLowerCase(),
taxId: v.string().regex(/^\d{2}-\d{5}$/),
phoneNumber: v.chain.regex(/^\+?[\d\s-]{10,}$/),
// Automatically imports { genders } from "../lib/constants"
gender: v.enum(v.var("constants.genders")),
};

2. Import and Register in Config

// prisma-guard.config.js
import { defineConfig } from "@explita/prisma-guard";
import { companyDecorators } from "@acme/validators";

export default defineConfig({
decorators: companyDecorators,
});

Community Decorators (Coming Soon)

As the Prisma Guard ecosystem grows, common validation presets will be shareable via public decorator packages:

  • @opensource/email-validators: Delivarability checks, MX lookups, or temp-mail exclusions.
  • @opensource/id-validators: Tax numbers, VAT IDs, or local SSN validation regex mappings.