Validation
Check user input against rules.
On this page
valk.validate checks input, usually decoded JSON or form data, against rules you build by chaining calls on a Field.
Check an email address#
use valk.validate
fn main() {
println(validate.email("ada@example.com")) // true
println(validate.email("ada@example")) // false
}
validate.email is a simple syntax check; it does not contact the mail server.
Validate an object#
use valk.json
use valk.validate
fn main() {
let rule = validate.Field.object(.{
"name" => validate.Field.string().min(1).max(50)
"email" => validate.Field.string().email()
"age" => validate.Field.int().min(18).optional()
"tags" => validate.Field.array(validate.Field.string()).max(5)
})
let data = json.decode("{ \"name\": \"Ada\", \"email\": \"nope\", \"age\": 12, \"tags\": [] }") ! return
let errors = Map[String]{}
if !rule.validate_and_translate(data, errors) {
each errors as message, field {
println(field + ": " + message)
}
}
// field.email: Invalid email address
// field.age: Minimum value is 18
}
validate returns whether the data passes, and records one error code per failing field in errors, keyed by its path. validate_and_translate also turns the codes into English messages. A third argument to validate replaces the field prefix.
Rules#
| Constructor | Accepts |
|---|---|
Field.string() |
Text |
Field.int(), Field.float() |
Numbers (an integer also passes as a float) |
Field.bool() |
true or false |
Field.array(of) |
An array, each item checked against of |
Field.object(fields) |
An object, each named field checked |
| Check | Meaning |
|---|---|
min(n), max(n) |
String byte length, array or object size, or number value |
utf8_min(n), utf8_max(n) |
String length in characters |
fmin(x), fmax(x) |
Float bounds |
email() |
A valid email address |
equals_string(text) |
Exactly this text |
lower(), upper() |
No uppercase / no lowercase letters |
username(numbers, underscore) |
Letters, plus digits and _ when allowed |
is_syntax(mask) |
Only these characters: a = a-z, A = A-Z, 0 = 0-9, others literal |
custom(func, code) |
func returns false: the field gets error code |
optional() |
The field may be absent |
nullable() |
null is accepted |
default(value) |
Filled in by try_cast when absent |
Custom checks and messages#
use valk.json
use valk.validate
fn main() {
let even = validate.Field.int().custom(fn(v: json.Value) bool {
return v.is_int && v.int % 2 == 0
}, "not_even")
let errors = Map[String]{}
even.validate(json.from(3), errors)
validate.Field.translate_errors(errors, .{ "not_even" => "Must be an even number" })
println(errors.get("field") !? "") // Must be an even number
}
translate_errors uses validate.default_translations unless you pass your own map of code to message, for example to show messages in another language.
Convert loose input#
use valk.json
use valk.validate
fn main() {
let rule = validate.Field.object(.{
"count" => validate.Field.int()
"notify" => validate.Field.bool().default(false)
})
let form = json.decode("{ \"count\": \"3\" }") ! return
let data = rule.try_cast(form)
println(rule.validate(data)) // true
println(json.encode(data)) // {"count":3,"notify":false}
}
try_cast converts values toward the rule's types, such as the text "3" to the number 3, and fills in defaults. Values that cannot be converted stay as they are, so validate afterwards.
get_form_rules() returns a rule's limits as JSON, for client-side form checks.
