Validation
Validation¶
env-loader-pro provides multiple validation mechanisms to ensure configuration correctness. Validation happens after type casting and before returning the configuration.
Validation Order¶
Validation is applied in this order:
- Required variables - Check if required variables exist
- Empty values - Check if required variables are empty
- Strict mode - Check for unknown variables
- Deprecated variables - Warn about deprecated variables
- Custom validators - Apply custom validation rules
- Regex validators - Apply regex pattern validation
- Policy-as-code - Apply policy enforcement (if enabled)
Required Variables¶
Ensure critical variables are present:
config = load_env(
required=["API_KEY", "DB_URI", "PORT"]
)
# Raises ValidationError if any required variable is missing
Missing Required Variables¶
If a required variable is missing, ValidationError is raised:
# .env: API_KEY=secret (but DB_URI missing)
config = load_env(required=["API_KEY", "DB_URI", "PORT"])
# Raises: ValidationError: Missing required environment variables: DB_URI, PORT
Empty Required Values¶
Empty values for required variables also raise an error:
# .env: API_KEY= (empty)
config = load_env(required=["API_KEY"])
# Raises: ValidationError: Required variable 'API_KEY' is empty
Empty vs Missing
- Missing: Variable not in config at all →
ValidationError - Empty: Variable exists but is
Noneor""→ValidationError
Validation Rules¶
Add custom validation logic with lambda functions or callables:
config = load_env(
types={"PORT": int},
rules={
"PORT": lambda v: 1024 < v < 65535,
"TIMEOUT": lambda v: v > 0,
"ALLOWED_HOSTS": lambda hosts: len(hosts) > 0
}
)
# Raises ValidationError if any rule returns False
Rule Function Signature¶
Validation rules receive the typed value (after type casting):
rules = {
# Integer validation
"PORT": lambda v: isinstance(v, int) and 1024 < v < 65535,
# String validation
"EMAIL": lambda v: "@" in v and "." in v.split("@")[1],
# List validation
"HOSTS": lambda hosts: isinstance(hosts, list) and len(hosts) > 0,
# Complex validation
"API_KEY": lambda key: len(key) >= 32 and key.startswith("sk-")
}
Rule Execution Order
Rules are executed after type casting. If type casting fails, rules are not executed.
Rule Error Messages¶
When a rule fails, ValidationError is raised with a descriptive message:
# .env: PORT=80
config = load_env(
types={"PORT": int},
rules={"PORT": lambda v: 1024 < v < 65535}
)
# Raises: ValidationError: Custom validation failed for 'PORT': 80
Handling Rule Exceptions¶
If a rule raises an exception, it's wrapped in ValidationError:
def validate_port(v):
if not isinstance(v, int):
raise TypeError("Port must be an integer")
return 1024 < v < 65535
config = load_env(
types={"PORT": int},
rules={"PORT": validate_port}
)
# If TypeError raised, wrapped as: ValidationError: Validation error for 'PORT': Port must be an integer
Strict Mode¶
Enable strict mode to catch unknown variables (typos, etc.):
config = load_env(
required=["API_KEY"],
types={"PORT": int},
strict=True
)
# Warns about variables in .env not in required/types/defaults
Strict Mode Behavior¶
- Warns (does not fail) about unknown variables by default
- Useful for catching typos in variable names
- Recommended for production environments
# .env: API_KEY=secret, PORT=8080, TYPO_VAR=value
config = load_env(
required=["API_KEY"],
types={"PORT": int},
strict=True
)
# Warning: Unknown variables found: TYPO_VAR
Strict Mode Default
By default, strict mode warns instead of failing. This can be changed in SchemaValidator initialization.
Regex Validators¶
Validate format with regex patterns:
import re
config = load_env(
regex_validators={
"EMAIL": r'^[\w\.-]+@[\w\.-]+\.\w+$',
"VERSION": r'^\d+\.\d+\.\d+$',
"IP_ADDRESS": r'^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$'
}
)
# Raises ValidationError if pattern doesn't match
Regex Validation Behavior¶
- Patterns are compiled once and reused
- Validation happens after type casting
- Empty values are not validated (use required check instead)
# .env: EMAIL=invalid-email
config = load_env(
regex_validators={"EMAIL": r'^[\w\.-]+@[\w\.-]+\.\w+$'}
)
# Raises: ValidationError: Regex validation failed for 'EMAIL': invalid-email
Deprecated Variables¶
Warn about deprecated variables:
config = load_env(
deprecated_vars=["OLD_API_KEY", "LEGACY_PORT"]
)
# Warns: Variable 'OLD_API_KEY' is deprecated and will be removed in a future version
Deprecation Strategy
Use deprecated variables to provide migration paths: 1. Mark old variable as deprecated 2. Support both old and new variables 3. Remove old variable in next major version
Schema Validation¶
Use Pydantic or dataclass for comprehensive validation:
from env_loader_pro import load_with_schema
from pydantic import BaseModel, validator
class Config(BaseModel):
port: int
email: str
@validator('port')
def validate_port(cls, v):
if not 1024 < v < 65535:
raise ValueError('Port must be between 1024 and 65535')
return v
@validator('email')
def validate_email(cls, v):
if '@' not in v:
raise ValueError('Invalid email')
return v
config = load_with_schema(Config)
# Pydantic validates all fields
Policy-as-Code Validation¶
Enforce policies via JSON/YAML files:
See Policy-as-Code for details.
Validation Error Messages¶
Clear error messages help debugging:
try:
config = load_env(required=["API_KEY"])
except ValidationError as e:
print(e)
# "Missing required environment variables: API_KEY"
Error Message Format¶
| Error Type | Message Format |
|---|---|
| Missing required | "Missing required environment variables: VAR1, VAR2" |
| Empty required | "Required variable 'VAR' is empty" |
| Custom validation | "Custom validation failed for 'VAR': value" |
| Regex validation | "Regex validation failed for 'VAR': value" |
| Unknown variables | "Unknown variables found: VAR1, VAR2" |
Best Practices¶
1. Validate Early¶
Check required variables at startup:
# Application startup
config = load_env(
required=["API_KEY", "DB_URI"],
types={"PORT": int},
rules={"PORT": lambda v: 1024 < v < 65535}
)
# Fail fast if configuration is invalid
2. Use Strict Mode in Production¶
Enable strict mode to catch typos:
3. Combine Validation Methods¶
Use multiple validation methods for comprehensive checks:
config = load_env(
required=["API_KEY"],
types={"PORT": int, "EMAIL": str},
rules={"PORT": lambda v: 1024 < v < 65535},
regex_validators={"EMAIL": r'^[\w\.-]+@[\w\.-]+\.\w+$'},
strict=True
)
4. Use Schemas for Complex Validation¶
For complex validation, use Pydantic schemas:
5. Handle Validation Errors Gracefully¶
try:
config = load_env(required=["API_KEY"])
except ValidationError as e:
logger.error(f"Configuration error: {e}")
sys.exit(1)
Common Mistakes¶
Mistake 1: Validating Before Type Casting¶
# ❌ WRONG: Rule receives string
rules={"PORT": lambda v: 1024 < int(v) < 65535}
# ✅ CORRECT: Rule receives typed value
types={"PORT": int}
rules={"PORT": lambda v: 1024 < v < 65535}
Mistake 2: Not Handling Empty Values¶
# ❌ WRONG: Empty string passes validation
rules={"API_KEY": lambda v: len(v) > 0}
# ✅ CORRECT: Use required check
required=["API_KEY"]
Mistake 3: Strict Mode Failing¶
# ❌ WRONG: Strict mode fails on unknown vars
config = load_env(strict=True) # Fails if any unknown vars
# ✅ CORRECT: Strict mode warns by default
# Or use warn_only=False to make it fail
Related Topics¶
- Schema Support - Type-safe validation
- Policy-as-Code - Policy enforcement
- Security Model - Security validation