Added AI skills
This commit is contained in:
137
.agents/skills/zod/references/compose-shared-schemas.md
Normal file
137
.agents/skills/zod/references/compose-shared-schemas.md
Normal file
@@ -0,0 +1,137 @@
|
||||
---
|
||||
title: Extract Shared Schemas into Reusable Modules
|
||||
impact: MEDIUM
|
||||
impactDescription: Duplicating schemas across files leads to inconsistency; shared schemas ensure single source of truth
|
||||
tags: compose, reuse, modules, organization
|
||||
---
|
||||
|
||||
## Extract Shared Schemas into Reusable Modules
|
||||
|
||||
When the same schema pattern appears in multiple places, extract it into a shared module. This ensures consistency, reduces duplication, and makes changes propagate automatically across your codebase.
|
||||
|
||||
**Incorrect (duplicating schemas):**
|
||||
|
||||
```typescript
|
||||
// api/users.ts
|
||||
import { z } from 'zod'
|
||||
|
||||
const userSchema = z.object({
|
||||
id: z.string().uuid(),
|
||||
email: z.string().email(),
|
||||
name: z.string().min(1),
|
||||
createdAt: z.date(),
|
||||
})
|
||||
|
||||
// api/orders.ts
|
||||
import { z } from 'zod'
|
||||
|
||||
const orderSchema = z.object({
|
||||
id: z.string().uuid(), // Duplicated
|
||||
userId: z.string().uuid(), // Same pattern
|
||||
items: z.array(z.object({
|
||||
productId: z.string().uuid(), // Duplicated
|
||||
quantity: z.number().int().positive(),
|
||||
})),
|
||||
createdAt: z.date(), // Duplicated
|
||||
})
|
||||
|
||||
// api/comments.ts
|
||||
import { z } from 'zod'
|
||||
|
||||
const commentSchema = z.object({
|
||||
id: z.string().uuid(), // Same duplication
|
||||
userId: z.string().uuid(),
|
||||
content: z.string().min(1),
|
||||
createdAt: z.date(), // Inconsistency risk
|
||||
})
|
||||
```
|
||||
|
||||
**Correct (shared schema modules):**
|
||||
|
||||
```typescript
|
||||
// schemas/common.ts
|
||||
import { z } from 'zod'
|
||||
|
||||
// Reusable ID types
|
||||
export const uuid = z.string().uuid()
|
||||
export type UUID = z.infer<typeof uuid>
|
||||
|
||||
// Timestamps
|
||||
export const timestamps = z.object({
|
||||
createdAt: z.date(),
|
||||
updatedAt: z.date(),
|
||||
})
|
||||
|
||||
// Base entity with ID
|
||||
export const baseEntity = z.object({
|
||||
id: uuid,
|
||||
}).merge(timestamps)
|
||||
|
||||
export type BaseEntity = z.infer<typeof baseEntity>
|
||||
|
||||
// Pagination
|
||||
export const paginationParams = z.object({
|
||||
page: z.coerce.number().int().positive().default(1),
|
||||
limit: z.coerce.number().int().min(1).max(100).default(20),
|
||||
})
|
||||
```
|
||||
|
||||
```typescript
|
||||
// schemas/user.ts
|
||||
import { z } from 'zod'
|
||||
import { baseEntity, uuid } from './common'
|
||||
|
||||
export const userSchema = baseEntity.extend({
|
||||
email: z.string().email(),
|
||||
name: z.string().min(1),
|
||||
})
|
||||
|
||||
export type User = z.infer<typeof userSchema>
|
||||
```
|
||||
|
||||
```typescript
|
||||
// schemas/order.ts
|
||||
import { z } from 'zod'
|
||||
import { baseEntity, uuid } from './common'
|
||||
|
||||
const orderItemSchema = z.object({
|
||||
productId: uuid,
|
||||
quantity: z.number().int().positive(),
|
||||
})
|
||||
|
||||
export const orderSchema = baseEntity.extend({
|
||||
userId: uuid,
|
||||
items: z.array(orderItemSchema).min(1),
|
||||
total: z.number().positive(),
|
||||
})
|
||||
|
||||
export type Order = z.infer<typeof orderSchema>
|
||||
```
|
||||
|
||||
**Organizing schema modules:**
|
||||
|
||||
```
|
||||
schemas/
|
||||
├── common.ts # Shared primitives and base schemas
|
||||
├── user.ts # User-related schemas
|
||||
├── order.ts # Order-related schemas
|
||||
├── product.ts # Product-related schemas
|
||||
└── index.ts # Re-exports for convenience
|
||||
```
|
||||
|
||||
```typescript
|
||||
// schemas/index.ts
|
||||
export * from './common'
|
||||
export * from './user'
|
||||
export * from './order'
|
||||
export * from './product'
|
||||
|
||||
// Usage
|
||||
import { userSchema, orderSchema, uuid, type User } from '@/schemas'
|
||||
```
|
||||
|
||||
**When NOT to use this pattern:**
|
||||
- One-off schemas used only in a single file
|
||||
- When schemas look similar but have different semantics (don't over-abstract)
|
||||
|
||||
Reference: [Zod - Type Inference](https://zod.dev/api#type-inference)
|
||||
Reference in New Issue
Block a user