config-validator
Validate environment variables and configuration with Pydantic, Zod. Type-safe config loading. Fail fast on missing vars. Keywords - environment variables, config validation, pydantic settings, zod config, env vars, type safe config
- 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: config-validator
description: Validate environment variables and configuration with Pydantic, Zod. Type-safe config loading. Fail fast on missing vars. Keywords - environment variables, config validation, pydantic settings, zod config, env vars, type safe config
---
# Config Validator
## When to Use This Skill
Activate when:
- Loading environment variables from .env files
- Need type-safe configuration validation
- Want to fail fast on missing/invalid config
- Setting up application configuration
- Need default values and documentation for config
## What This Skill Does
Validates configuration with:
- Type-safe environment variable loading
- Required vs optional variables
- Default values and validation rules
- Fail-fast on startup (missing/invalid config)
- Auto-completion for config (TypeScript/Python types)
- Configuration documentation
## Supported Technologies
**Python**:
- Pydantic Settings (recommended)
- python-decouple
- environs
**Node/Bun**:
- Zod (recommended)
- @t3-oss/env-nextjs
- envalid
## Example: Pydantic Settings (Python)
```python
# config.py
from pydantic_settings import BaseSettings, SettingsConfigDict
from pydantic import Field, PostgresDsn, RedisDsn, EmailStr, validator
from typing import Optional
class Settings(BaseSettings):
"""Application configuration with validation"""
model_config = SettingsConfigDict(
env_file='.env',
env_file_encoding='utf-8',
case_sensitive=False
)
# Application
app_name: str = Field(default="My App", description="Application name")
environment: str = Field(default="development", pattern="^(development|staging|production)quot;)
debug: bool = Field(default=False, description="Enable debug mode")
# Server
host: str = Field(default="0.0.0.0")
port: int = Field(default=8000, ge=1, le=65535)
# Database
database_url: PostgresDsn = Field(..., description="PostgreSQL connection string")
database_pool_size: int = Field(default=10, ge=1, le=100)
# Redis
redis_url: RedisDsn = Field(..., description="Redis connection string")
# Auth
secret_key: str = Field(..., min_length=32, description="Secret key for JWT")
algorithm: str = Field(default="HS256")
access_token_expire_minutes: int = Field(default=30, ge=1)
# Email
smtp_host: str = Field(...)
smtp_port: int = Field(default=587, ge=1, le=65535)
smtp_user: EmailStr = Field(...)
smtp_password: str = Field(...)
# External APIs
stripe_api_key: Optional[str] = Field(default=None, description="Stripe API key (optional)")
# Feature flags
enable_registration: bool = Field(default=True)
enable_email_verification: bool = Field(default=True)
@validator('environment')
def validate_environment(cls, v):
"""Ensure environment is valid"""
allowed = ['development', 'staging', 'production']
if v not in allowed:
raise ValueError(f"environment must be one of {allowed}")
return v
@validator('secret_key')
def validate_secret_key(cls, v, values):
"""Ensure secret key is strong in production"""
if values.get('environment') == 'production' and len(v) < 32:
raise ValueError("secret_key must be at least 32 characters in production")
return v
# Load and validate config
try:
settings = Settings()
except Exception as e:
print(f"Configuration error: {e}")
exit(1)
# Usage
print(f"Starting {settings.app_name} on {settings.host}:{settings.port}")
```
## Example: .env File
```bash
# .env
APP_NAME=MyApp
ENVIRONMENT=production
DEBUG=false
HOST=0.0.0.0
PORT=8000
DATABASE_URL=postgresql://user:pass@localhost:5432/mydb
DATABASE_POOL_SIZE=20
REDIS_URL=redis://localhost:6379/0
SECRET_KEY=your-super-secret-key-at-least-32-characters-long
ACCESS_TOKEN_EXPIRE_MINUTES=30
SMTP_HOST=smtp.gmail.com
SMTP_PORT=587
SMTP_USER=noreply@example.com
SMTP_PASSWORD=your-smtp-password
STRIPE_API_KEY=sk_live_...
ENABLE_REGISTRATION=true
ENABLE_EMAIL_VERIFICATION=true
```
## Example: Zod Config Validation (TypeScript)
```typescript
// config.ts
import { z } from 'zod';
import dotenv from 'dotenv';
dotenv.config();
const envSchema = z.object({
// Application
NODE_ENV: z.enum(['development', 'staging', 'production']).default('development'),
APP_NAME: z.string().default('MyApp'),
PORT: z.coerce.number().int().min(1).max(65535).default(3000),
// Database
DATABASE_URL: z.string().url().startsWith('postgresql://'),
DATABASE_POOL_SIZE: z.coerce.number().int().min(1).max(100).default(10),
// Redis
REDIS_URL: z.string().url().startsWith('redis://'),
// Auth
JWT_SECRET: z.string().min(32, 'JWT_SECRET must be at least 32 characters'),
JWT_EXPIRES_IN: z.string().default('30m'),
// Email
SMTP_HOST: z.string(),
SMTP_PORT: z.coerce.number().int().default(587),
SMTP_USER: z.string().email(),
SMTP_PASSWORD: z.string(),
// External APIs
STRIPE_API_KEY: z.string().startsWith('sk_').optional(),
// Feature flags
ENABLE_REGISTRATION: z.coerce.boolean().default(true),
ENABLE_EMAIL_VERIFICATION: z.coerce.boolean().default(true),
});
// Custom refinement for production
const configSchema = envSchema.refine(
(data) => {
if (data.NODE_ENV === 'production') {
return data.JWT_SECRET.length >= 64; // Stricter in production
}
return true;
},
{
message: 'JWT_SECRET must be at least 64 characters in production',
path: ['JWT_SECRET'],
}
);
// Parse and validate
let config: z.infer<typeof configSchema>;
try {
config = configSchema.parse(process.env);
} catch (error) {
if (error instanceof z.ZodError) {
console.error('❌ Configuration validation failed:');
error.errors.forEach((err) => {
console.error(` - ${err.path.join('.')}: ${err.message}`);
});
process.exit(1);
}
throw error;
}
export default config;
// Usage
console.log(`Starting ${config.APP_NAME} on port ${config.PORT}`);
```
## Example: T3 Env (Next.js/TypeScript)
```typescript
// env.mjs
import { createEnv } from "@t3-oss/env-nextjs";
import { z } from "zod";
export const env = createEnv({
/**
* Server-side environment variables (never exposed to client)
*/
server: {
DATABASE_URL: z.string().url(),
JWT_SECRET: z.string().min(32),
STRIPE_SECRET_KEY: z.string().startsWith("sk_"),
NODE_ENV: z.enum(["development", "staging", "production"]),
},
/**
* Client-side environment variables (exposed to browser)
* Must be prefixed with NEXT_PUBLIC_
*/
client: {
NEXT_PUBLIC_APP_NAME: z.string().default("MyApp"),
NEXT_PUBLIC_API_URL: z.string().url(),
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY: z.string().startsWith("pk_"),
},
/**
* Manual destructuring for Next.js
*/
runtimeEnv: {
DATABASE_URL: process.env.DATABASE_URL,
JWT_SECRET: process.env.JWT_SECRET,
STRIPE_SECRET_KEY: process.env.STRIPE_SECRET_KEY,
NODE_ENV: process.env.NODE_ENV,
NEXT_PUBLIC_APP_NAME: process.env.NEXT_PUBLIC_APP_NAME,
NEXT_PUBLIC_API_URL: process.env.NEXT_PUBLIC_API_URL,
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY: process.env.NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY,
},
/**
* Skip validation during build (optional)
*/
skipValidation: !!process.env.SKIP_ENV_VALIDATION,
});
// Usage
console.log(env.DATABASE_URL); // ✅ Type-safe
console.log(env.NEXT_PUBLIC_APP_NAME); // ✅ Type-safe
// console.log(env.SOME_RANDOM_VAR); // ❌ TypeScript error
```
## Example: Config with Nested Objects (Python)
```python
# config/settings.py
from pydantic import BaseModel
from pydantic_settings import BaseSettings
class DatabaseConfig(BaseModel):
url: str
pool_size: int = 10
echo: bool = False
class RedisConfig(BaseModel):
url: str
max_connections: int = 50
class EmailConfig(BaseModel):
smtp_host: str
smtp_port: int = 587
smtp_user: str
smtp_password: str
from_email: str = "noreply@example.com"
class Settings(BaseSettings):
app_name: str = "MyApp"
debug: bool = False
database: DatabaseConfig
redis: RedisConfig
email: EmailConfig
model_config = SettingsConfigDict(env_nested_delimiter='__')
# .env file
# DATABASE__URL=postgresql://...
# DATABASE__POOL_SIZE=20
# REDIS__URL=redis://...
# EMAIL__SMTP_HOST=smtp.gmail.com
settings = Settings()
print(settings.database.url)
print(settings.redis.max_connections)
```
## Best Practices
1. **Fail fast on startup** - Validate config immediately, exit if invalid
2. **Use type-safe schemas** - Pydantic (Python), Zod (TypeScript)
3. **Required vs optional** - Make critical vars required, provide defaults for others
4. **Validate values** - Not just types (min/max, patterns, URLs)
5. **Document variables** - Add descriptions for each var
6. **Never commit .env** - Add to .gitignore, use .env.example
7. **Different configs per environment** - .env.development, .env.production
8. **Load early** - Before app initialization
## Example: .env.example (Template)
```bash
# .env.example
# Copy to .env and fill in values
# Application
APP_NAME=MyApp
ENVIRONMENT=development # development | staging | production
DEBUG=true
# Database (required)
DATABASE_URL=postgresql://user:password@localhost:5432/mydb
# Redis (required)
REDIS_URL=redis://localhost:6379/0
# Auth (required)
SECRET_KEY=generate-a-secure-random-key-at-least-32-chars
ACCESS_TOKEN_EXPIRE_MINUTES=30
# Email (required)
SMTP_HOST=smtp.gmail.com
SMTP_PORT=587
SMTP_USER=your-email@gmail.com
SMTP_PASSWORD=your-app-password
# External APIs (optional)
STRIPE_API_KEY=sk_test_...
# Feature Flags
ENABLE_REGISTRATION=true
ENABLE_EMAIL_VERIFICATION=true
```
## Common Validation Patterns
```python
# URL validation
database_url: PostgresDsn # Must be valid PostgreSQL URL
# Email validation
smtp_user: EmailStr # Must be valid email
# Integer with range
port: int = Field(ge=1, le=65535) # Between 1-65535
# String with pattern
environment: str = Field(pattern="^(dev|staging|prod)quot;)
# String with min length
secret_key: str = Field(min_length=32)
# Optional with default
debug: bool = Field(default=False)
# Custom validator
@validator('api_key')
def validate_api_key(cls, v):
if not v.startswith('sk_'):
raise ValueError('API key must start with sk_')
return v
```
## Integration with Other Skills
- **api-endpoint-builder** - Load API configuration
- **auth-flow-builder** - Validate JWT secret and expiration
- **database-query-optimizer** - Load database connection config
- **cache-strategy-builder** - Load Redis configuration
---
**Version**: 1.0.0
**Category**: Backend Extended
**Complexity**: Low-Medium