Claude Code Plugins

Community-maintained marketplace

Feedback

Create Zod schemas for new entities/resources in the backend. Use when adding a new resource, creating data models, defining DTOs (CreateType, UpdateType), or setting up validation schemas. Triggers on "new schema", "add entity", "create model", "define type".

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 create-schema
description Create Zod schemas for new entities/resources in the backend. Use when adding a new resource, creating data models, defining DTOs (CreateType, UpdateType), or setting up validation schemas. Triggers on "new schema", "add entity", "create model", "define type".

Create Schema

Creates Zod schemas with TypeScript type inference for new entities in this backend template.

Quick Reference

Location: src/schemas/{entity-name}.schema.ts Naming: Singular, kebab-case (e.g., note.schema.ts, course-registration.schema.ts)

Instructions

Step 1: Create the Schema File

Create a new file at src/schemas/{entity-name}.schema.ts

Step 2: Define the Entity ID Schema

Always start with a dedicated ID schema:

import { z } from "zod";

export const {entity}IdSchema = z.string();
export type {Entity}IdType = z.infer<typeof {entity}IdSchema>;

Step 3: Define the Base Entity Schema

export const {entity}Schema = z.object({
  id: {entity}IdSchema,                    // Primary key
  // ... entity-specific fields ...
  createdBy: userIdSchema,                 // If entity is user-owned (import from user.schemas)
  createdAt: z.date().optional(),          // Set by DB/service
  updatedAt: z.date().optional(),          // Set by DB/service
});

export type {Entity}Type = z.infer<typeof {entity}Schema>;

Step 4: Define Create DTO Schema

Omit system-managed fields and add validation:

export const create{Entity}Schema = {entity}Schema
  .omit({
    id: true,           // Generated by service/system
    createdBy: true,    // Set from authenticated user context
    createdAt: true,    // Set by service/database
    updatedAt: true,    // Set by service/database
  })
  .extend({
    // Add stricter validation for required fields
    fieldName: z.string().min(1, "{Entity} field is required for creation."),
  });
export type Create{Entity}Type = z.infer<typeof create{Entity}Schema>;

Step 5: Define Update DTO Schema

Make all mutable fields optional:

export const update{Entity}Schema = {entity}Schema
  .omit({
    id: true,           // Part of URL, not body
    createdBy: true,    // Immutable
    createdAt: true,    // Immutable
    updatedAt: true,    // Set by service/database
  })
  .partial();           // All fields optional for updates
export type Update{Entity}Type = z.infer<typeof update{Entity}Schema>;

Step 6: Define Query Parameters Schema (if needed)

Extend the base query params for filtering:

import { queryParamsSchema } from "./shared.schema";

export const {entity}QueryParamsSchema = queryParamsSchema.extend({
  // Entity-specific filters
  createdBy: userIdSchema.optional(),
  status: z.enum(["active", "inactive"]).optional(),
});
export type {Entity}QueryParamsType = z.infer<typeof {entity}QueryParamsSchema>;

Patterns & Rules

Naming Conventions

  • File name: {entity-name}.schema.ts (singular, kebab-case)
  • Schema variables: {entity}Schema, create{Entity}Schema (camelCase)
  • Type names: {Entity}Type, Create{Entity}Type (PascalCase)

Import Rules

  • Always use path aliases: import { x } from "@/schemas/..."
  • Import shared schemas from ./shared.schema
  • Import user-related schemas from ./user.schemas when needed

Schema Design Rules

  1. Always export both schema and type for each definition
  2. ID schemas are separate - allows reuse and type narrowing
  3. Timestamps are optional on the base schema (not present on creation)
  4. Create schemas omit system-managed fields (id, createdBy, timestamps)
  5. Update schemas are partial - all fields optional
  6. Extend base queryParamsSchema for entity-specific filters

Validation Guidelines

  • Add .min(1) for required string fields in create schemas
  • Use .optional() for truly optional fields
  • Use z.coerce.number() for numeric query params (they come as strings)
  • Add descriptive error messages: z.string().min(1, "Field is required")

Cross-Reference Pattern

When referencing other entities:

import { userIdSchema } from "./user.schemas";
import { otherEntityIdSchema } from "./other-entity.schema";

export const myEntitySchema = z.object({
  id: myEntityIdSchema,
  ownerId: userIdSchema, // Reference to user
  relatedId: otherEntityIdSchema, // Reference to another entity
});

Complete Example

See src/schemas/note.schema.ts for a complete reference implementation.

import { z } from "zod";
import { queryParamsSchema } from "./shared.schema";
import { userIdSchema } from "./user.schemas";

// ID Schema
export const noteIdSchema = z.string();
export type NoteIdType = z.infer<typeof noteIdSchema>;

// Base Entity Schema
export const noteSchema = z.object({
  id: noteIdSchema,
  content: z.string(),
  createdBy: userIdSchema,
  createdAt: z.date().optional(),
  updatedAt: z.date().optional(),
});
export type NoteType = z.infer<typeof noteSchema>;

// Create DTO
export const createNoteSchema = noteSchema
  .omit({
    id: true,
    createdBy: true,
    createdAt: true,
    updatedAt: true,
  })
  .extend({
    content: z.string().min(1, "Note content is required for creation."),
  });
export type CreateNoteType = z.infer<typeof createNoteSchema>;

// Update DTO
export const updateNoteSchema = noteSchema
  .omit({
    id: true,
    createdBy: true,
    createdAt: true,
    updatedAt: true,
  })
  .partial();
export type UpdateNoteType = z.infer<typeof updateNoteSchema>;

// Query Parameters
export const noteQueryParamsSchema = queryParamsSchema.extend({
  createdBy: userIdSchema.optional(),
});
export type NoteQueryParamsType = z.infer<typeof noteQueryParamsSchema>;

Shared Schemas Reference

The following are available from @/schemas/shared.schema:

  • queryParamsSchema - Base pagination/sorting params (search, sortBy, sortOrder, page, limit)
  • paginatedResultsSchema(dataSchema) - Generic paginated response wrapper
  • entityIdParamSchema(paramName) - URL path parameter validation
  • DEFAULT_PAGE, DEFAULT_LIMIT - Pagination defaults

What NOT to Do

  • Do NOT use process.env - environment config belongs in src/env.ts
  • Do NOT create barrel exports (index.ts) - use explicit imports
  • Do NOT use plural names (notes.schema.ts) - use singular (note.schema.ts)
  • Do NOT define business logic in schemas - schemas are for structure/validation only
  • Do NOT skip type exports - always export both schema and inferred type