Claude Code Plugins

Community-maintained marketplace

Feedback

Guide through creating Zod validation schemas with i18n integration for API request validation. Use when creating API endpoints that need validation.

Install Skill

1Download skill
2Enable skills in Claude

Open claude.ai/settings/capabilities and find the "Skills" section

3Upload to Claude

Click "Upload skill" and select the downloaded ZIP file

Note: Please verify skill by going through its instructions before using it.

SKILL.md

name zod-schema-creation
description Guide through creating Zod validation schemas with i18n integration for API request validation. Use when creating API endpoints that need validation.

Zod Validation Schema Creation Skill

Guides through creating Zod validation schemas with i18n integration for API request validation.

Purpose

USE this skill when:

  • Creating new API endpoints that accept request bodies
  • Adding validation for POST, PUT, or PATCH requests
  • Implementing form validation schemas

Critical Rules

⚠️ Factory pattern REQUIRED - Schemas must accept translation function t: (key: string) => string ⚠️ i18n keys in errors - All error messages use translation keys, not hardcoded text ⚠️ File organization - All schemas in shared/utils/schemas/ directory ⚠️ Type export - Always export TypeScript type using z.infer<ReturnType<...>>

Workflow Steps

Step 1: Identify Validation Requirements

  • What fields are required/optional?
  • What validation rules apply?
  • Creating (create schema) or updating (update schema)?

Step 2: Create Schema File

Location: shared/utils/schemas/{resource}.ts

Step 3: Implement Factory Function Pattern

import { z } from "zod"

export function createSpeakerSchema(t: (key: string) => string) {
  return z.object({
    firstName: z
      .string()
      .min(1, t("validation.firstNameRequired"))
      .max(100, t("validation.firstNameTooLong")),
    
    lastName: z
      .string()
      .min(1, t("validation.lastNameRequired"))
      .max(100),
    
    email: z
      .string()
      .email(t("validation.emailInvalid"))
      .optional(),
  })
}

Step 4: Export TypeScript Type

export type CreateSpeakerInput = z.infer<ReturnType<typeof createSpeakerSchema>>

Step 5: Create Update Schema (If Needed)

export function updateSpeakerSchema(t: (key: string) => string) {
  return createSpeakerSchema(t).partial()
}

export type UpdateSpeakerInput = z.infer<ReturnType<typeof updateSpeakerSchema>>

Step 6: Update Barrel File

Add to shared/utils/schemas/index.ts:

export * from "./speaker"

Step 7: Add Translation Keys

Use i18n-key-validation skill to ensure keys exist in both pl.json and en.json

Step 8: Implement in API Route

import { createSpeakerSchema } from "#shared/utils/schemas"

export default defineEventHandler(async (event) => {
  const body = await validateBody(event, createSpeakerSchema)
  
  // body is now typed and validated
  const db = useDrizzle()
  const [speaker] = await db.insert(tables.speakers)
    .values({
      id: crypto.randomUUID(),
      ...body,
      createdAt: new Date(),
      updatedAt: new Date(),
    })
    .returning()
  
  return speaker
})

Common Validation Patterns

// String with length
field: z.string().min(1, t("validation.required")).max(200)

// Email
email: z.string().email(t("validation.emailInvalid")).optional()

// Number
age: z.number().min(0).max(150)

// Enum
status: z.enum(["active", "archived"])

// Array
tags: z.array(z.string()).max(10)

Anti-Patterns

❌ Hardcoded error messages ❌ No factory pattern ❌ Wrong file location ❌ Missing type export

References

  • Backend guidelines: server/AGENTS.md
  • i18n patterns: .agents/i18n-patterns.md