data-validator-generator
This skill generates type-safe data validation schemas for APIs and forms. Supports Python (Pydantic), TypeScript (Zod), JavaScript (Joi, Yup), Go (validator). Creates request/response schemas, form validation, environment variable validation, and config validation. Activate when user says "validate data", "create schema", "input validation", "type-safe", or building APIs/forms.
- Category: discovered
- Tags: github, community
- Version: 1.0.0
- Author: walid
- Downloads: 0
- Updated: 2025-11-22
Download the latest version (v1.0.0) from this page.
SKILL.md
---
name: data-validator-generator
description: |
This skill generates type-safe data validation schemas for APIs and forms.
Supports Python (Pydantic), TypeScript (Zod), JavaScript (Joi, Yup), Go (validator).
Creates request/response schemas, form validation, environment variable validation, and config validation.
Activate when user says "validate data", "create schema", "input validation", "type-safe", or building APIs/forms.
Output: Complete validation schemas with types, error messages, examples, and tests.
---
# Data Validator Generator
> **Purpose**: Generate type-safe validation schemas with clear error messages
---
## When to Use This Skill
Activate when:
- ✅ Building API endpoints (request/response validation)
- ✅ Creating forms (frontend validation)
- ✅ Validating environment variables (.env)
- ✅ Validating configuration files (JSON, YAML)
- ✅ User says: "validate input", "create schema", "type-safe validation"
- ✅ Need to ensure data integrity and prevent invalid data
---
## What This Skill Does
**Generates validation schemas with**:
1. **Type definitions** - Typed schemas (string, number, email, etc.)
2. **Constraints** - Min/max length, regex patterns, ranges
3. **Required/optional** - Mark fields as required or optional
4. **Nested objects** - Complex nested structures
5. **Arrays** - Array validation with item schemas
6. **Custom validators** - Custom validation logic
7. **Error messages** - Clear, user-friendly error messages
8. **Transformations** - Trim, lowercase, parse dates
9. **Tests** - Unit tests for validation logic
---
## Supported Technologies
### Python
- **Pydantic** (recommended) - Type-safe, FastAPI integration
- **Marshmallow** - Serialization + validation
- **Cerberus** - Lightweight validation
### TypeScript/JavaScript
- **Zod** (recommended for TypeScript) - Type inference, composable
- **Yup** - Schema validation, React Hook Form compatible
- **Joi** - Enterprise validation (Node.js)
- **Ajv** - JSON Schema validator (fastest)
### Go
- **validator** - Struct tag validation
- **go-playground/validator** - Comprehensive validation
---
## Validation Patterns
### 1. API Request Validation (Pydantic - FastAPI)
```python
from pydantic import BaseModel, Field, validator, EmailStr
from typing import Optional, List
from datetime import datetime
from enum import Enum
class UserRole(str, Enum):
ADMIN = "admin"
USER = "user"
GUEST = "guest"
class CreateUserRequest(BaseModel):
"""User creation request schema"""
email: EmailStr = Field(..., description="User email address")
password: str = Field(..., min_length=8, max_length=100, description="Password (min 8 chars)")
name: str = Field(..., min_length=2, max_length=100, description="Full name")
age: Optional[int] = Field(None, ge=18, le=120, description="Age (18-120)")
role: UserRole = Field(UserRole.USER, description="User role")
tags: List[str] = Field(default_factory=list, max_items=10, description="User tags")
# Custom validator
@validator('password')
def password_must_contain_uppercase(cls, v):
if not any(c.isupper() for c in v):
raise ValueError('Password must contain at least one uppercase letter')
return v
@validator('email')
def email_must_be_lowercase(cls, v):
return v.lower()
class Config:
schema_extra = {
"example": {
"email": "user@example.com",
"password": "SecurePass123",
"name": "John Doe",
"age": 25,
"role": "user",
"tags": ["developer", "python"]
}
}
class UserResponse(BaseModel):
"""User response schema"""
id: int
email: str
name: str
age: Optional[int]
role: UserRole
created_at: datetime
class Config:
from_attributes = True # For SQLAlchemy models
json_encoders = {
datetime: lambda v: v.isoformat()
}
# Usage in FastAPI
from fastapi import APIRouter, HTTPException
router = APIRouter()
@router.post("/users", response_model=UserResponse, status_code=201)
async def create_user(user: CreateUserRequest):
# Pydantic validates automatically
# If validation fails, returns 422 with detailed errors
return {"id": 1, **user.dict(), "created_at": datetime.now()}
```
**Validation Error Response** (automatic):
```json
{
"detail": [
{
"loc": ["body", "email"],
"msg": "value is not a valid email address",
"type": "value_error.email"
},
{
"loc": ["body", "password"],
"msg": "Password must contain at least one uppercase letter",
"type": "value_error"
}
]
}
```
---
### 2. Frontend Form Validation (Zod - TypeScript)
```typescript
import { z } from 'zod';
// Define schema
const createUserSchema = z.object({
email: z.string()
.email('Invalid email address')
.toLowerCase()
.trim(),
password: z.string()
.min(8, 'Password must be at least 8 characters')
.max(100, 'Password must be less than 100 characters')
.regex(/[A-Z]/, 'Password must contain at least one uppercase letter')
.regex(/[0-9]/, 'Password must contain at least one number'),
name: z.string()
.min(2, 'Name must be at least 2 characters')
.max(100, 'Name too long')
.trim(),
age: z.number()
.int('Age must be an integer')
.min(18, 'Must be 18 or older')
.max(120, 'Invalid age')
.optional(),
role: z.enum(['admin', 'user', 'guest'])
.default('user'),
tags: z.array(z.string())
.max(10, 'Maximum 10 tags allowed')
.default([]),
acceptTerms: z.boolean()
.refine((val) => val === true, 'You must accept the terms'),
birthDate: z.string()
.datetime('Invalid date format')
.transform((str) => new Date(str))
.refine((date) => date < new Date(), 'Birth date must be in the past')
});
// Infer TypeScript type from schema
type CreateUserInput = z.infer<typeof createUserSchema>;
// Validate data
function validateUser(data: unknown): CreateUserInput {
try {
return createUserSchema.parse(data); // Throws ZodError if invalid
} catch (error) {
if (error instanceof z.ZodError) {
console.error('Validation errors:', error.errors);
// Format errors for UI
const formattedErrors = error.errors.map(err => ({
field: err.path.join('.'),
message: err.message
}));
throw new Error(JSON.stringify(formattedErrors));
}
throw error;
}
}
// Safe parse (returns { success: boolean, data/error })
function safeValidateUser(data: unknown) {
const result = createUserSchema.safeParse(data);
if (!result.success) {
return {
success: false,
errors: result.error.errors.map(err => ({
field: err.path.join('.'),
message: err.message
}))
};
}
return {
success: true,
data: result.data
};
}
// React Hook Form integration
import { useForm } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
function CreateUserForm() {
const {
register,
handleSubmit,
formState: { errors }
} = useForm<CreateUserInput>({
resolver: zodResolver(createUserSchema)
});
const onSubmit = (data: CreateUserInput) => {
console.log('Valid data:', data);
};
return (
<form onSubmit={handleSubmit(onSubmit)}>
<input {...register('email')} placeholder="Email" />
{errors.email && <span>{errors.email.message}</span>}
<input {...register('password')} type="password" placeholder="Password" />
{errors.password && <span>{errors.password.message}</span>}
<button type="submit">Create User</button>
</form>
);
}
```
---
### 3. Environment Variable Validation (Zod)
```typescript
import { z } from 'zod';
const envSchema = z.object({
// Server
NODE_ENV: z.enum(['development', 'production', 'test']).default('development'),
PORT: z.string().transform(Number).pipe(z.number().int().min(1).max(65535)).default('3000'),
HOST: z.string().default('localhost'),
// Database
DATABASE_URL: z.string().url('Invalid database URL'),
DB_POOL_SIZE: z.string().transform(Number).pipe(z.number().int().min(1).max(100)).default('10'),
// Redis
REDIS_URL: z.string().url('Invalid Redis URL'),
REDIS_TTL: z.string().transform(Number).pipe(z.number().int().min(0)).default('3600'),
// Auth
JWT_SECRET: z.string().min(32, 'JWT secret must be at least 32 characters'),
JWT_EXPIRES_IN: z.string().default('7d'),
// External APIs
API_KEY: z.string().min(1, 'API key is required'),
API_URL: z.string().url('Invalid API URL'),
// Feature flags
ENABLE_CACHING: z.string().transform(val => val === 'true').default('true'),
ENABLE_LOGGING: z.string().transform(val => val === 'true').default('true')
});
// Validate environment variables at startup
function validateEnv() {
try {
const env = envSchema.parse(process.env);
return env;
} catch (error) {
console.error('❌ Invalid environment variables:');
if (error instanceof z.ZodError) {
error.errors.forEach(err => {
console.error(` ${err.path.join('.')}: ${err.message}`);
});
}
process.exit(1);
}
}
// Export validated env
export const env = validateEnv();
// Usage
console.log(`Server running on port ${env.PORT}`);
console.log(`Database: ${env.DATABASE_URL}`);
```
---
### 4. Nested Object Validation (Pydantic)
```python
from pydantic import BaseModel, Field, validator
from typing import List, Optional
from datetime import datetime
class Address(BaseModel):
street: str = Field(..., min_length=5)
city: str = Field(..., min_length=2)
state: str = Field(..., min_length=2, max_length=2)
zip_code: str = Field(..., regex=r'^\d{5}(-\d{4})?#39;)
country: str = Field(default="USA")
class PaymentMethod(BaseModel):
type: str = Field(..., regex=r'^(credit_card|debit_card|paypal|stripe)#39;)
last_four: str = Field(..., regex=r'^\d{4}#39;)
expires_at: datetime
class OrderItem(BaseModel):
product_id: int = Field(..., gt=0)
quantity: int = Field(..., ge=1, le=100)
price: float = Field(..., gt=0)
@validator('price')
def price_must_have_two_decimals(cls, v):
return round(v, 2)
class CreateOrderRequest(BaseModel):
user_id: int = Field(..., gt=0)
items: List[OrderItem] = Field(..., min_items=1, max_items=50)
shipping_address: Address
billing_address: Optional[Address] = None
payment_method: PaymentMethod
discount_code: Optional[str] = Field(None, max_length=20)
notes: Optional[str] = Field(None, max_length=500)
@validator('billing_address', always=True)
def set_billing_address(cls, v, values):
# Default billing to shipping if not provided
return v or values.get('shipping_address')
@validator('items')
def calculate_total(cls, items):
total = sum(item.quantity * item.price for item in items)
if total > 10000:
raise ValueError('Order total exceeds maximum allowed ($10,000)')
return items
class Config:
schema_extra = {
"example": {
"user_id": 123,
"items": [
{"product_id": 1, "quantity": 2, "price": 29.99},
{"product_id": 2, "quantity": 1, "price": 49.99}
],
"shipping_address": {
"street": "123 Main St",
"city": "New York",
"state": "NY",
"zip_code": "10001"
},
"payment_method": {
"type": "credit_card",
"last_four": "1234",
"expires_at": "2025-12-31T23:59:59Z"
}
}
}
```
---
## Best Practices
1. **Use Type-Safe Libraries**
- Python: Pydantic (auto type coercion)
- TypeScript: Zod (type inference)
- Avoid runtime type checking when compile-time is available
2. **Clear Error Messages**
- Be specific: "Email is invalid" > "Validation failed"
- Include field name: "password: Must be at least 8 characters"
- Provide examples: "Date must be in format YYYY-MM-DD"
3. **Validate Early**
- API: Validate at endpoint entry (before business logic)
- Frontend: Validate on blur + on submit
- Config: Validate at app startup (fail fast)
4. **Transform Data**
- Trim whitespace: `email.trim()`
- Normalize: `email.toLowerCase()`
- Parse: Convert strings to numbers/dates
5. **Reuse Schemas**
- DRY: Define once, use for request + response
- Compose: Build complex schemas from simple ones
- Extend: Use inheritance/composition
---
## Common Validation Rules
```typescript
// String validations
z.string()
.min(3) // Minimum length
.max(100) // Maximum length
.email() // Email format
.url() // URL format
.uuid() // UUID format
.regex(/^[A-Z]/) // Regex pattern
.startsWith('http') // Prefix
.endsWith('.com') // Suffix
.trim() // Remove whitespace
.toLowerCase() // Transform to lowercase
// Number validations
z.number()
.int() // Integer only
.positive() // > 0
.negative() // < 0
.min(18) // Minimum value
.max(100) // Maximum value
.multipleOf(5) // Divisible by 5
// Date validations
z.date()
.min(new Date('2020-01-01')) // After date
.max(new Date()) // Before date
// Array validations
z.array(z.string())
.min(1) // At least 1 item
.max(10) // At most 10 items
.nonempty() // Not empty
// Object validations
z.object({ ... })
.strict() // No extra keys
.partial() // All keys optional
.required() // All keys required
// Custom validation
z.string().refine(
(val) => val.includes('@'),
{ message: 'Must contain @' }
)
```
---
## Integration with Other Skills
- **api-endpoint-builder** - Validate API requests/responses
- **form-builder-with-validation** - Validate form inputs
- **auth-flow-builder** - Validate credentials
- **orm-query-builder** - Validate database models
---
**Skill Version**: 1.0.0
**Technologies**: Python (Pydantic), TypeScript (Zod), JavaScript (Yup, Joi)
**Output**: Type-safe validation schemas with error messages and tests