Skip to content
Back to skills

Create Schema

ASecurity

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".

  • 666 stars
  • 0 votes
  • 1 copy
  • 3 views
  • Added February 8, 2026
databasestypescriptdatabasebackend

Security analysis

A100/100

Scanned February 12, 2026

npx -y skills add majiayu000/claude-skill-registry --skill create-schema --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Create Schema?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Create Schema
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/majiayu000-create-schema/badge)](https://www.skillsdirectory.com/skills/majiayu000-create-schema)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
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:

```typescript
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

```typescript
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:

```typescript
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:

```typescript
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:

```typescript
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:

```typescript
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.

```typescript
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

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.