Data validation is a critical aspect of any robust application. Rye provides a powerful validation dialect that allows you to validate and transform data according to specified rules.
The validation dialect in Rye is centered around the validate
function, which takes two arguments: the data to validate (usually a dictionary) and a block of validation rules.
validate dict { name: "John" age: 30 } {
name: required
age: required integer
}
; returns: dict { name: "John" age: 30 }
When validation succeeds, it returns a dictionary with the validated and potentially transformed values. If validation fails, it returns an error.
The most basic validation rules are required
and optional
:
; Ensure a value is present
validate dict { name: "John" } { name: required }
; returns: dict { name: "John" }
; Validation fails if a required value is missing
validate dict { } { name: required }
; returns: error with status 403 and message "validation error"
; Provide a default value if not present
validate dict { } { name: optional "Anonymous" }
; returns: dict { name: "Anonymous" }
; Use both required and optional fields
validate dict { name: "John" } {
name: required
age: optional 30
}
; returns: dict { name: "John" age: 30 }
The validation dialect can validate and convert values to specific types:
; Convert string to integer
validate dict { age: "30" } { age: required integer }
; returns: dict { age: 30 }
; Convert to decimal
validate dict { price: "19.99" } { price: required decimal }
; returns: dict { price: 19.99 }
; Ensure value is a string
validate dict { name: "John" } { name: required string }
; returns: dict { name: "John" }
; Validate email format
validate dict { email: "john@example.com" } { email: required email }
; returns: dict { email: "john@example.com" }
; Validate and convert date format
validate dict { birthdate: "30.12.2024" } { birthdate: required date }
; returns: dict with birthdate as a date object
You can use the check
rule to validate a value against a condition:
; Ensure age is less than 100
validate dict { age: 30 } { age: required integer check { < 100 } }
; returns: dict { age: 30 }
; Validation fails if condition is not met
validate dict { age: 150 } { age: required integer check { < 100 } }
; returns: error
The calc
rule allows you to transform a value using a calculation:
; Add 10 to the age
validate dict { age: 30 } { age: required integer calc { + 10 } }
; returns: dict { age: 40 }
; Convert temperature from Celsius to Fahrenheit
validate dict { temp_c: 25 } { temp_c: required decimal calc { * 1.8 + 32 } }
; returns: dict { temp_c: 77.0 }
For lists, you can use the some
rule to apply validation to each item:
; Validate a list of integers
validate list [1 "2" 3] { some { required integer } }
; returns: list [1 2 3]
When validation fails, it returns an error with status 403 and a message “validation error”. You can use error handling functions to get more details:
; Get the error status
validate dict { email: "not-an-email" } { email: required email } |disarm |status?
; returns: 403
; Get the error message
validate dict { email: "not-an-email" } { email: required email } |disarm |message?
; returns: "validation error"
; Get detailed error information
validate dict { email: "not-an-email" } { email: required email } |disarm |details?
; returns: dict { email: "not email" }
The validate>ctx
function works like validate
but returns a context object for easier field access:
ctx: validate>ctx dict { name: "John" age: 30 } {
name: required
age: required integer
}
; Access fields directly
ctx -> 'name
; returns: "John"
ctx -> 'age
; returns: 30
You can combine multiple validation rules for more complex validations:
validate dict {
name: "John"
age: "30"
email: "john@example.com"
} {
name: required string
age: required integer check { > 0 check { < 120 } }
email: required email
}
; returns: validated dictionary with converted values
Here’s a more complete example showing how validation might be used in a web application:
; Define a function to handle user registration
register-user: func [request] [
; Validate the request data
user-data: validate>ctx request/body {
username: required string
email: required email
password: required string
age: optional 0 integer check { >= 18 }
}
; If validation succeeded, proceed with registration
if error? user-data [
return response 400 user-data
]
; Use the validated data
create-user user-data
response 201 "User registered successfully"
]
The validation dialect provides a powerful yet concise way to ensure your data meets your requirements, with automatic type conversion and detailed error reporting when validation fails.