Teaches how to validate external data at runtime using Zod and other validation libraries in TypeScript. Use when working with APIs, JSON parsing, user input, or any external data source where runtime validation is needed.
Validate external data at runtime using Zod to prevent crashes from unvalidated API responses, JSON parsing, or user input. Use when working with APIs, parsing JSON, or handling any external data sources.
/plugin marketplace add djankies/claude-configs/plugin install typescript@claude-configsThis skill is limited to using the following tools:
references/error-handling.mdreferences/performance.mdreferences/zod-patterns.mdconst data: User = await fetch("/api/user").then(r => r.json());
This compiles, but if the API returns { username: string } instead of { name: string }, your code crashes at runtime.
Solution: Runtime validation libraries that:
Recommended Library: Zod (modern, TypeScript-first, best DX) </overview>
<workflow> ## Runtime Validation FlowStep 1: Define Schema
Create a schema describing the expected shape:
import { z } from "zod";
const UserSchema = z.object({
id: z.string(),
name: z.string(),
email: z.string().email(),
age: z.number().int().positive()
});
Step 2: Extract TypeScript Type
type User = z.infer<typeof UserSchema>;
Step 3: Validate at Runtime
const data = await fetch("/api/user").then(r => r.json());
const result = UserSchema.safeParse(data);
if (result.success) {
const user: User = result.data;
console.log(user.name);
} else {
console.error("Validation failed:", result.error);
}
Step 4: Handle Validation Errors
if (!result.success) {
const issues = result.error.issues.map(issue => ({
path: issue.path.join("."),
message: issue.message
}));
throw new ValidationError("Invalid user data", issues);
}
</workflow>
<examples>
## Example 1: API Response Validation
Setup
import { z } from "zod";
const UserSchema = z.object({
id: z.string().uuid(),
name: z.string().min(1),
email: z.string().email(),
age: z.number().int().min(0).max(150),
role: z.enum(["admin", "user", "guest"]),
createdAt: z.string().datetime()
});
type User = z.infer<typeof UserSchema>;
Validation
async function fetchUser(id: string): Promise<User> {
const response = await fetch(`/api/users/${id}`);
if (!response.ok) {
throw new Error(`HTTP ${response.status}`);
}
const data: unknown = await response.json();
const result = UserSchema.safeParse(data);
if (!result.success) {
throw new Error(`Invalid user data: ${result.error.message}`);
}
return result.data;
}
const LoginFormSchema = z.object({
email: z.string().email("Invalid email address"),
password: z.string().min(8, "Password must be at least 8 characters"),
rememberMe: z.boolean().optional()
});
type LoginForm = z.infer<typeof LoginFormSchema>;
function validateLoginForm(formData: unknown): LoginForm {
return LoginFormSchema.parse(formData);
}
const form = {
email: "user@example.com",
password: "securepassword123"
};
try {
const validated = validateLoginForm(form);
await login(validated);
} catch (error) {
if (error instanceof z.ZodError) {
error.issues.forEach(issue => {
console.error(`${issue.path.join(".")}: ${issue.message}`);
});
}
}
const AddressSchema = z.object({
street: z.string(),
city: z.string(),
state: z.string().length(2),
zipCode: z.string().regex(/^\d{5}(-\d{4})?$/)
});
const UserWithAddressSchema = z.object({
id: z.string(),
name: z.string(),
email: z.string().email(),
address: AddressSchema,
billingAddress: AddressSchema.optional()
});
type UserWithAddress = z.infer<typeof UserWithAddressSchema>;
const TagSchema = z.string().min(1).max(20);
const PostSchema = z.object({
id: z.string(),
title: z.string().min(1).max(200),
content: z.string(),
tags: z.array(TagSchema).min(1).max(10),
metadata: z.record(z.string(), z.unknown())
});
type Post = z.infer<typeof PostSchema>;
async function fetchPosts(): Promise<Post[]> {
const response = await fetch("/api/posts");
const data: unknown = await response.json();
const PostsSchema = z.array(PostSchema);
return PostsSchema.parse(data);
}
Union Types: Use discriminated unions for API responses with success/error states.
Transforms: Convert strings to dates or coerce query parameters to numbers.
Refinements: Add custom validation logic for complex business rules (e.g., password strength).
Partial Schemas: Create update schemas from full schemas using .partial().
See references/zod-patterns.md for complete examples of unions, transforms, refinements, and partial schemas.
</examples>
For complete patterns see references/:
references/zod-patterns.md - Complete Zod API referencereferences/error-handling.md - Validation error handling strategiesreferences/performance.md - Validation performance optimizationFor related skills:
Cross-Plugin References:
safeParse for error handling, not parse (unless throwing is desired)SHOULD:
NEVER:
as Type) on unvalidated datanpm install zod
pnpm add zod
yarn add zod
TypeScript configuration:
{
"compilerOptions": {
"strict": true
}
}
</installation>
<validation>
## Validation Implementation Checklist
Schema Definition: Match expected structure, use appropriate validators, add custom refinements.
Error Handling: Use safeParse, log errors with context, provide user-friendly messages.
Type Safety: Derive types with z.infer, avoid manual assertions after validation.
Performance: Reuse schemas, validate at boundaries only, avoid redundant checks.
Testing: Test valid data, invalid data, and edge cases.
See references/error-handling.md for error handling patterns and references/performance.md for optimization techniques.
</validation>
io-ts (functional programming style)
import * as t from "io-ts";
const UserCodec = t.type({
id: t.string,
name: t.string,
email: t.string
});
type User = t.TypeOf<typeof UserCodec>;
Yup (common with Formik)
import * as yup from "yup";
const userSchema = yup.object({
name: yup.string().required(),
email: yup.string().email().required()
});
AJV (JSON Schema)
import Ajv from "ajv";
const ajv = new Ajv();
const validate = ajv.compile({
type: "object",
properties: {
name: { type: "string" },
email: { type: "string", format: "email" }
},
required: ["name", "email"]
});
Recommendation: Use Zod for new TypeScript projects. Best DX and type inference. </alternatives>
<common-patterns> ## Common PatternsValidation Middleware: Validate request bodies in Express/framework handlers before processing.
Safe JSON Parse: Combine JSON.parse with schema validation for type-safe parsing.
Configuration Validation: Validate environment variables and config at startup.
See references/zod-patterns.md for complete implementations and additional patterns.
</common-patterns>
This skill should be used when the user asks to "create a slash command", "add a command", "write a custom command", "define command arguments", "use command frontmatter", "organize commands", "create command with file references", "interactive command", "use AskUserQuestion in command", or needs guidance on slash command structure, YAML frontmatter fields, dynamic arguments, bash execution in commands, user interaction patterns, or command development best practices for Claude Code.
This skill should be used when the user asks to "create an agent", "add an agent", "write a subagent", "agent frontmatter", "when to use description", "agent examples", "agent tools", "agent colors", "autonomous agent", or needs guidance on agent structure, system prompts, triggering conditions, or agent development best practices for Claude Code plugins.
This skill should be used when the user asks to "create a hook", "add a PreToolUse/PostToolUse/Stop hook", "validate tool use", "implement prompt-based hooks", "use ${CLAUDE_PLUGIN_ROOT}", "set up event-driven automation", "block dangerous commands", or mentions hook events (PreToolUse, PostToolUse, Stop, SubagentStop, SessionStart, SessionEnd, UserPromptSubmit, PreCompact, Notification). Provides comprehensive guidance for creating and implementing Claude Code plugin hooks with focus on advanced prompt-based hooks API.