Data Validation
Runtime validation is one of the core features of farrow-schema, keeping your application type-safe at runtime.
🛡️ Basic Validation
typescript
import { Validator } from 'farrow-schema/validator'
class User extends ObjectType {
name = String
age = Number
}
// Validate data
const result = Validator.validate(User, {
name: 'Alice',
age: 25
})
// Use result.kind for type narrowing
if (result.kind === 'Ok') {
console.log('Validation successful:', result.value)
// result.value type is { name: string, age: number }
} else {
console.log('Validation failed:', result.value.message)
console.log('Error location:', result.value.path?.join('.'))
}Validation Options
Strict Mode vs Lenient Mode
typescript
// Strict mode (default): No type conversion
const strictResult = Validator.validate(User, {
name: 'Bob',
age: '30' // ❌ String fails in strict mode
})
// Lenient mode: Attempt type conversion
const lenientResult = Validator.validate(User, {
name: 'Bob',
age: '30' // ✅ String will be converted to number
}, { strict: false })Type conversion rules (lenient mode strict: false):
"25"→25"true"→true"2024-01-01"→Date
Create Dedicated Validators
When using in APIs, it's recommended to create dedicated validators:
typescript
import { createSchemaValidator } from 'farrow-schema/validator'
// Create validator (strict mode by default)
const validateUser = createSchemaValidator(User)
// Or explicitly specify lenient mode
const validateUserLenient = createSchemaValidator(User, { strict: false })
// Use in API
app.post('/users', (req, res) => {
const result = validateUser(req.body)
if (result.kind === 'Err') {
return res.status(400).json({
error: result.value.message,
path: result.value.path
})
}
// result.value has passed type checking
const user = await createUser(result.value)
res.json(user)
})Custom Validators
Using ValidatorType
typescript
import { ValidatorType } from 'farrow-schema/validator'
// Email validator
class EmailType extends ValidatorType<string> {
validate(input: unknown) {
const result = Validator.validate(String, input)
if (result.kind === 'Err') return result
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/
if (!emailRegex.test(result.value)) {
return this.Err('Invalid email format')
}
return this.Ok(result.value)
}
}
// Use
class User extends ObjectType {
name = String
email = EmailType // Use custom validator
}Parameterized Validators
typescript
// String length validator
const StringLength = (min: number, max: number) => {
return class StringLength extends ValidatorType<string> {
validate(input: unknown) {
const result = Validator.validate(String, input)
if (result.kind === 'Err') return result
if (result.value.length < min || result.value.length > max) {
return this.Err(`Length must be between ${min}-${max} characters`)
}
return this.Ok(result.value)
}
}
}
class Article extends ObjectType {
title = StringLength(5, 100)
content = StringLength(50, 5000)
}Regular Expression Validators
typescript
import { RegExp } from 'farrow-schema/validator'
class User extends ObjectType {
username = RegExp(/^[a-zA-Z0-9_]{3,16}$/)
phone = RegExp(/^1[3-9]\d{9}$/)
}Error Handling
Understanding Validation Results
typescript
const result = Validator.validate(User, data)
// Recommended: Use kind field for type narrowing
if (result.kind === 'Err') {
console.log('Error:', result.value.message) // Error message
console.log('Path:', result.value.path?.join('.')) // Error path, e.g., "user.address.zipCode"
return
}
// result.value is type-safe
console.log(result.value)Practical Example: Complete Error Handling
typescript
function validateAndCreate(data: unknown): Result<User, string> {
const result = Validator.validate(CreateUserInput, data)
// Use kind for type narrowing
if (result.kind === 'Err') {
return Err(`Validation failed: ${result.value.message}`)
}
// Business logic validation
if (result.value.age < 18) {
return Err('User must be at least 18 years old')
}
return Ok(createUser(result.value))
}
// Unified handling
const result = validateAndCreate(req.body)
if (result.kind === 'Err') {
return res.status(400).json({ error: result.value })
}
res.json(result.value)