Practical Examples
Learn application scenarios of farrow-schema through real cases.
REST API
typescript
import { ObjectType, String, Number, List, Optional } from 'farrow-schema'
import { Validator } from 'farrow-schema/validator'
// Define model
class User extends ObjectType {
id = String
name = String
email = String
age = Number
tags = List(String)
}
// Create input (exclude auto-generated fields)
const CreateUserInput = omitObject(User, ['id'])
// API endpoint
app.post('/users', async (req, res) => {
// Validate input
const result = Validator.validate(CreateUserInput, req.body)
if (result.kind === 'Err') {
return res.status(400).json({
error: 'Validation failed',
message: result.value.message,
path: result.value.path
})
}
// result.value is type-safe
const newUser = await db.users.create({
id: generateId(),
...result.value
})
res.json(newUser)
})
// Update input (all fields optional)
const UpdateUserInput = partialObject(
omitObject(User, ['id'])
)
app.patch('/users/:id', async (req, res) => {
const result = Validator.validate(UpdateUserInput, req.body)
if (result.kind === 'Err') {
return res.status(400).json({ error: result.value.message })
}
const updated = await db.users.update(req.params.id, result.value)
res.json(updated)
})Form Validation
typescript
class SignUpForm extends ObjectType {
username = RegExp(/^[a-zA-Z0-9_]{3,16}$/)
email = EmailType
password = StringLength(8, 32)
confirmPassword = String
}
// Custom validator: check password matching
class SignUpValidator extends ValidatorType<TypeOf<typeof SignUpForm>> {
validate(input: unknown) {
const result = Validator.validate(SignUpForm, input)
if (result.kind === 'Err') return result
if (result.value.password !== result.value.confirmPassword) {
return this.Err('Passwords do not match')
}
return this.Ok(result.value)
}
}
// Use in frontend
async function handleSubmit(formData: unknown) {
const result = Validator.validate(SignUpValidator, formData)
if (result.kind === 'Err') {
showError(result.value.message)
return
}
await api.signUp(result.value)
}Configuration Validation
typescript
import { Union, Literal, Struct } from 'farrow-schema'
const DatabaseConfig = Struct({
type: Literal('postgres'),
host: String,
port: Int,
database: String,
user: String,
password: String
})
const RedisConfig = Struct({
type: Literal('redis'),
host: String,
port: Int,
password: Optional(String)
})
const AppConfig = Struct({
port: Int,
database: Union(DatabaseConfig, RedisConfig),
logging: Struct({
level: Union(
Literal('debug'),
Literal('info'),
Literal('warn'),
Literal('error')
),
format: Union(
Literal('json'),
Literal('text')
)
})
})
// Load configuration
const config = loadConfigFile('app.json')
const result = Validator.validate(AppConfig, config)
if (result.kind === 'Err') {
throw new Error(`Invalid config: ${result.value.message} at ${result.value.path}`)
}
// Type-safe configuration
startServer(result.value)Tree Data Structure
typescript
class Category extends ObjectType {
id = String
name = String
description = Optional(String)
children = List(Category) // ✅ Recursive reference
parent = Optional(Category)
}
// Validate category tree
const categoryTree = {
id: '1',
name: 'Electronics',
description: 'Electronic products',
children: [
{
id: '1-1',
name: 'Computers',
children: [
{ id: '1-1-1', name: 'Laptops', children: [] }
]
}
]
}
const result = Validator.validate(Category, categoryTree)
if (result.kind === 'Ok') {
// Recursively traverse category tree
function traverse(category: TypeOf<typeof Category>) {
console.log(category.name)
category.children.forEach(traverse)
}
traverse(result.value)
}Complex Business Scenario: E-commerce Order
typescript
import { ObjectType, String, Number, List, Union, Literal, Optional } from 'farrow-schema'
// Product
class Product extends ObjectType {
id = String
name = String
price = Number
stock = Int
}
// Order item
class OrderItem extends ObjectType {
product = Product
quantity = Int
subtotal = Number
}
// Address
class Address extends ObjectType {
street = String
city = String
state = String
zipCode = String
country = String
}
// Payment method
const PaymentMethod = Union(
Struct({
type: Literal('credit_card'),
cardNumber: String,
cardHolder: String,
expiryDate: String
}),
Struct({
type: Literal('paypal'),
email: String
}),
Struct({
type: Literal('bank_transfer'),
bankName: String,
accountNumber: String
})
)
// Order status
const OrderStatus = Union(
Literal('pending'),
Literal('paid'),
Literal('shipped'),
Literal('delivered'),
Literal('cancelled')
)
// Complete order
class Order extends ObjectType {
id = String
userId = String
items = List(OrderItem)
shippingAddress = Address
billingAddress = Optional(Address)
paymentMethod = PaymentMethod
status = OrderStatus
total = Number
createdAt = String
updatedAt = String
}
// Create order input
const CreateOrderInput = Struct({
items: List(Struct({
productId: String,
quantity: Int
})),
shippingAddress: Address,
billingAddress: Optional(Address),
paymentMethod: PaymentMethod
})
// API handling
app.post('/orders', async (req, res) => {
const result = Validator.validate(CreateOrderInput, req.body)
if (result.kind === 'Err') {
return res.status(400).json({
error: 'Invalid order data',
details: result.value.message,
path: result.value.path
})
}
const order = await createOrder(result.value)
res.status(201).json(order)
})Multi-level Nested Validation
typescript
class Company extends ObjectType {
id = String
name = String
departments = List(Department)
}
class Department extends ObjectType {
id = String
name = String
manager = Employee
employees = List(Employee)
}
class Employee extends ObjectType {
id = String
name = String
email = String
position = String
salary = Number
projects = List(Project)
}
class Project extends ObjectType {
id = String
name = String
deadline = String
status = Union(
Literal('not_started'),
Literal('in_progress'),
Literal('completed')
)
}
// Validate entire company structure
const companyData = loadCompanyData()
const result = Validator.validate(Company, companyData)
if (result.kind === 'Ok') {
console.log(`Company ${result.value.name} validation passed`)
console.log(`Total ${result.value.departments.length} departments`)
} else {
console.error(`Data validation failed: ${result.value.message}`)
console.error(`Error location: ${result.value.path?.join('.')}`)
}