validate
import validate
Schema-based input validation for Zuri applications.
The validate module provides a fluent, chainable API for defining
field-level validation rules, composing them into schemas, and checking
arbitrary data dictionaries against those schemas.
Quick start
import validate
var schema = validate.schema({
name: validate.required().string().max_length(100),
email: validate.required().string().email(),
age: validate.required().integer().between(18, 120),
website: validate.string().url().sometimes(),
})
var result = schema.check({
name: 'Ada Lovelace',
email: 'ada@example.com',
age: 36,
})
if result.valid {
echo 'All good.'
} else {
for err in result.errors {
echo '${err.field}: ${err.message}'
}
}
Rule chaining
Every top-level function returns a Validator instance. Rules are
evaluated in the order they are chained. By default, evaluation stops at
the first failing rule for each field (bail mode). Pass .bail(false)
to collect all errors for a field.
# Collects every error for "password" rather than stopping at the first.
var rules = validate.schema({
password: validate.required().string()
.min_length(8)
.regex('/[A-Z]/')
.regex('/[0-9]/')
.bail(false),
})
Optional fields
var rules = validate.schema({
# bio is optional; when present it must be at most 500 chars.
bio: validate.string().max_length(500).sometimes(),
# nickname is optional and may be nil.
nickname: validate.nullable().string().max_length(30).sometimes(),
})
Cross-field validation
var schema = validate.schema({
password: validate.required().string().min_length(8),
password_confirmation: validate.required().string().confirmed(),
role: validate.required().string().is_in(['user', 'admin']),
admin_code: validate.required_if('role', 'admin').string(),
})
required_if() (like the other conditional-requirement functions below)
must come before .string() in the chain: see “Sequential Validation”
further down for why the order here matters.
Nested fields (dot-notation)
var schema = validate.schema({
'address.street': validate.required().string(),
'address.city': validate.required().string(),
'address.country': validate.required().string().length(2).uppercase(),
})
List items (wildcard)
var schema = validate.schema({
'tags.*': validate.required().string().max_length(32).alpha_dash(),
})
Custom rules
Inline function:
var schema = validate.schema({
code: validate.required().string().custom(@(value) {
return value.starts_with('ZURI-') and value.length() == 12
}, 'Must be a valid Zuri code (ZURI-XXXXXXX).')
})
Subclassing Rule:
import validate { Rule, Validator }
class Slug < Rule {
validate(value) {
if !is_string(value) return false
return !!value.match('/^[a-z0-9]+(?:-[a-z0-9]+)*$/')
}
error() { return 'The ${self.name} field must be a valid slug.' }
}
var schema = validate.schema({
slug: Validator().required().use(Slug),
})
Raising on failure
import validate { ValidationError }
def create_user(req, res) {
catch {
schema.check_or_raise(req.body)
} as e
if e {
return res.json({ errors: schema.group_errors(e.errors) }, 422)
}
return res.json({ ok: true })
}
HTTP handler pattern
import http
import validate
import validate { ValidationError }
var create_user_schema = validate.schema({
name: validate.required().string().max_length(100),
email: validate.required().string().email(),
password: validate.required().string().min_length(8),
role: validate.required().string().is_in(['user', 'admin']).sometimes(),
})
server.handle('POST', '/users', @(req, res) {
catch {
create_user_schema.check_or_raise(req.body)
} as e
if e {
return res.json({ errors: create_user_schema.group_errors(e.errors) }, 422)
}
# ... create user
res.json({ status: 'created' }, 201)
})
Sequential Validation
The validation mode is sequential, meaning that each field is validated in turn. This is the behavior you expect from a form submission. It is important to keep this in mind when using this module.
Consider the example below:
var schema = validate.schema({
name: validate.required_if('age', 30).string().max_length(100),
age: validate.required().integer().between(18, 120),
})
If the age field is empty, the validation of name will not raise any
error. This is because the required_if rule is only applied if the age
field is 30 or greater and the entire chain exists before the next rule
string().
However, if the validation rules were modified like this:
var schema = validate.schema({
name: validate.string().required_if('age', 30).max_length(100),
age: validate.required().integer().between(18, 120),
})
Even when the age field is empty, the validation of name will raise
an error if it is empty. This is because the string rule comes before
the required_if rule.
With this behavior in mind, it is advised that optional rules such as
sometimes, nullable, *_if, *_unless rules are applied early in
the chain unless you absolutely want them to fail irrespective.
The validate API
Every public name in validate, wherever it is declared. Each links to
the page that documents it.
| Name | Kind | Summary |
|---|---|---|
validate.Rule | class | Abstract base class for all validation rules. |
validate.Schema | class | Binds a ruleset to data and validates it. |
validate.ValidationError | class | Raised by Schema.check_or_raise() when validation fails. |
validate.Validator | class | Fluent rule chain builder for a single field. |
validate.accepted | function | The value must be one of the accepted truthy representations: true, "true", "yes", "on", "1", 1. |
validate.after | function | The value must be a date string after ref. |
validate.after_or_equal | function | The value must be a date string after or equal to ref. |
validate.alpha | function | The string must contain only ASCII alphabetic characters (a-z, A-Z). |
validate.alpha_dash | function | The string must contain only ASCII alphanumeric characters, hyphens, and underscores. |
validate.alpha_num | function | The string must contain only ASCII alphanumeric characters. |
validate.before | function | The value must be a date string before ref. |
validate.before_or_equal | function | The value must be a date string before or equal to ref. |
validate.between | function | The value’s size must be between min and max (inclusive). |
validate.boolean | function | The value must be boolean-like: true, false, "true", "false", "1", "0", 1, or 0. |
validate.confirmed | function | The value must match the ``field_name_confirmation sibling field. |
validate.contains | function | The string must contain the given substring. |
validate.custom | function | Validates with an inline anonymous function. |
validate.custom_with_data | function | Validates with an inline function that also receives the full data dictionary, enabling cross-field logic… |
validate.date | function | The value must be a valid date string parseable by the date module. |
validate.declined | function | The value must be one of the declined falsy representations: false, "false", "no", "off", "0", 0. |
validate.dict | function | The value must be a dictionary. |
validate.different | function | The value must differ from the value of other_field. |
validate.distinct | function | The list must not contain duplicate values. |
validate.doesnt_contain | function | The string must not contain the given substring. |
validate.doesnt_end_with | function | The string must not end with any of the given suffixes. |
validate.doesnt_start_with | function | The string must not start with any of the given prefixes. |
validate.each | function | Every item in the list must pass the given Validator chain. |
validate.email | function | The value must be a syntactically valid email address. |
validate.ends_with | function | The string must end with one of the given suffixes. |
validate.equals | function | The value must strictly equal expected. |
validate.gt | function | The numeric value must be greater than n. |
validate.gte | function | The numeric value must be greater than or equal to n. |
validate.in_list | function | Every item in the list value must be contained in values. |
validate.integer | function | The value must be an integer (no fractional part). |
validate.ip | function | The value must be a valid IPv4 or IPv6 address. |
validate.ipv4 | function | The value must be a valid IPv4 address. |
validate.ipv6 | function | The value must be a valid IPv6 address. |
validate.is_in | function | The value must be one of the given allowed values (strict comparison). |
validate.is_nil | function | The value must be nil. |
validate.json | function | The value must be a valid JSON string. |
validate.length | function | The string’s character count must equal exactly n. |
validate.length_between | function | The string’s length must be between min and max characters (inclusive). |
validate.list | function | The value must be a list. |
validate.lowercase | function | The string must be entirely lowercase. |
validate.lt | function | The numeric value must be less than n. |
validate.lte | function | The numeric value must be less than or equal to n. |
validate.max | function | The value’s size must be at most max. |
validate.max_items | function | The list must have at most max items. |
validate.max_length | function | The string must be at most max characters long. |
validate.min | function | The value’s size must be at least min. |
validate.min_items | function | The list must have at least min items. |
validate.min_length | function | The string must be at least min characters long. |
validate.multiple_of | function | The numeric value must be a multiple of n. |
validate.negative | function | The numeric value must be strictly negative (less than zero). |
validate.negative_or_zero | function | The numeric value must be zero or negative. |
validate.not_blank | function | The string must not consist entirely of whitespace. |
validate.not_equals | function | The value must not equal forbidden. |
validate.not_in | function | The value must not be one of the given forbidden values. |
validate.not_nil | function | The value must not be nil. |
validate.not_regex | function | The string must not match the given regular expression pattern. |
validate.nullable | function | A nil value passes all subsequent rules without evaluation. |
validate.number | function | The value must be a number (integer or float). |
validate.numeric | function | The value must be numeric: either a number type or a string that converts cleanly to a number. |
validate.positive | function | The numeric value must be strictly positive (greater than zero). |
validate.positive_or_zero | function | The numeric value must be zero or positive. |
validate.prohibited | function | This field must be absent or nil: it is never permitted. |
validate.prohibited_if | function | The field must be absent when other_field equals any of other_values. |
validate.prohibited_unless | function | The field must be absent unless other_field equals any of other_values. |
validate.prohibits | function | When this field is present, none of the listed sibling fields may also be present. |
validate.regex | function | The string must match the given regular expression pattern. |
validate.required | function | The field must be present, non-nil, and non-empty. |
validate.required_if | function | The field becomes required when other_field equals any value in other_values. |
validate.required_unless | function | The field becomes required unless other_field equals any value in other_values. |
validate.required_with | function | The field becomes required when any of the listed sibling fields are present and non-blank. |
validate.required_with_all | function | The field becomes required when all of the listed sibling fields are present and non-blank. |
validate.required_without | function | The field becomes required when any of the listed sibling fields are absent or blank. |
validate.required_without_all | function | The field becomes required when all of the listed sibling fields are absent or blank. |
validate.rule.instantiate_rule | function | Builds one Rule instance from a [rule_class, ...args] entry: the shape Validator._rules stores each… |
validate.rules.Accepted | class | Passes when the value equals one of the specified accepted values: true, "true", "yes", "on", "1",… |
validate.rules.After | class | Passes when the value is a date string after the given reference date. |
validate.rules.AfterOrEqual | class | Passes when the value is a date string after or equal to the given reference date. |
validate.rules.Alpha | class | Passes when the string contains only ASCII alphabetic characters (a-z, A-Z). |
validate.rules.AlphaDash | class | Passes when the string contains only ASCII alphanumeric characters, hyphens (-), and underscores (_). |
validate.rules.AlphaNum | class | Passes when the string contains only ASCII alphanumeric characters. |
validate.rules.Before | class | Passes when the value is a date string before the given reference date. |
validate.rules.BeforeOrEqual | class | Passes when the value is a date string before or equal to the given reference date. |
validate.rules.Between | class | Passes when the value’s size is between min and max (inclusive). |
validate.rules.BooleanRule | class | Passes only when the value is a boolean (true or false). |
validate.rules.Confirmed | class | Passes when the field’s value matches ``name_confirmation in the data. |
validate.rules.Contains | class | Passes when the string contains the given substring. |
validate.rules.CustomRule | class | Custom rule backed by a caller-supplied function. |
validate.rules.CustomRuleWithData | class | Custom rule that also receives the full data dictionary. |
validate.rules.DateRule | class | Passes when the value is a valid date string parseable by the date module (e.g. "2024-06-15",… |
validate.rules.Declined | class | Passes when the value equals one of the specified declined values: false, "false", "no", "off",… |
validate.rules.DictRule | class | Passes only when the value is a dictionary. |
validate.rules.Different | class | Passes when the field’s value does not match the value of another field. |
validate.rules.Distinct | class | Passes when the list field contains no duplicate values. |
validate.rules.DoesntContain | class | Passes when the string does not contain the given substring. |
validate.rules.DoesntEndWith | class | Passes when the string does not end with any of the given suffixes. |
validate.rules.DoesntStartWith | class | Passes when the string does not start with any of the given prefixes. |
validate.rules.Each | class | Passes when every item in the list satisfies the given Validator chain. |
validate.rules.Email | class | Passes when the value is a syntactically valid email address. |
validate.rules.EndsWith | class | Passes when the string ends with one of the given suffixes. |
validate.rules.Equals | class | Passes when the field value is strictly equal to the given expected value. |
validate.rules.Gt | class | Passes when the numeric value is greater than n. |
validate.rules.Gte | class | Passes when the numeric value is greater than or equal to n. |
validate.rules.In | class | Passes when the value is contained in the given list of allowed values. |
validate.rules.InList | class | Passes when every item in the value list is contained in the allowed list. |
validate.rules.IntegerRule | class | Passes only when the value is an integer (no fractional part). |
validate.rules.Ip | class | Passes when the value is a valid IPv4 or IPv6 address. |
validate.rules.Ipv4 | class | Passes when the value is a valid IPv4 address. |
validate.rules.Ipv6 | class | Passes when the value is a valid IPv6 address. |
validate.rules.Json | class | Passes when the value is a valid JSON string. |
validate.rules.Length | class | Passes when a string’s length is exactly n characters. |
validate.rules.LengthBetween | class | Passes when a string’s length is between min and max characters (inclusive). |
validate.rules.ListRule | class | Passes only when the value is a list. |
validate.rules.Lowercase | class | Passes when the string value contains only lowercase characters. |
validate.rules.Lt | class | Passes when the numeric value is less than n. |
validate.rules.Lte | class | Passes when the numeric value is less than or equal to n. |
validate.rules.Max | class | Passes when the value’s size is at most max. |
validate.rules.MaxItems | class | Passes when the list field has at most max items. |
validate.rules.MaxLength | class | Passes when a string’s length is at most max characters. |
validate.rules.Min | class | Passes when the value’s size is at least min. |
validate.rules.MinItems | class | Passes when the list field has at least min items. |
validate.rules.MinLength | class | Passes when a string’s length is at least min characters. |
validate.rules.MultipleOf | class | Passes when the numeric value is a multiple of n. |
validate.rules.Negative | class | Passes when the numeric value is negative (strictly less than zero). |
validate.rules.NegativeOrZero | class | Passes when the numeric value is negative or zero. |
validate.rules.Nil | class | Passes when the value is nil. |
validate.rules.NotBlank | class | Passes when the value does not consist solely of whitespace. |
validate.rules.NotEquals | class | Passes when the field value is not equal to the given forbidden value. |
validate.rules.NotIn | class | Passes when the value is not contained in the given list of values. |
validate.rules.NotNil | class | Passes when the value is not nil. |
validate.rules.NotRegex | class | Passes when the string value does not match the given regular expression. |
validate.rules.Nullable | class | Passes when the field is absent or nil. |
validate.rules.NumberRule | class | Passes only when the value is a number (integer or float). |
validate.rules.NumericRule | class | Passes only when the value is numeric (a number, or a string that can be losslessly converted to a number). |
validate.rules.Positive | class | Passes when the numeric value is positive (strictly greater than zero). |
validate.rules.PositiveOrZero | class | Passes when the numeric value is positive or zero. |
validate.rules.Prohibited | class | Passes when this field is absent or nil. |
validate.rules.ProhibitedIf | class | Passes when this field is absent or nil if another field equals one of the given values. |
validate.rules.ProhibitedUnless | class | Passes when this field is absent or nil unless another field equals one of the given values. |
validate.rules.Prohibits | class | Passes when both this field and the listed sibling fields are either all present (non-nil) or all absent (nil… |
validate.rules.Regex | class | Passes when the string value matches the given regular expression. |
validate.rules.Required | class | Fails when the field is absent, nil, an empty string, or an empty list. |
validate.rules.RequiredIf | class | Passes when this field is present and non-nil only if another field in the data satisfies a given condition. |
validate.rules.RequiredUnless | class | Passes when this field is present and non-nil unless another field equals any of the given values. |
validate.rules.RequiredWith | class | Passes when this field is present and non-nil if any of the listed sibling fields are also present and… |
validate.rules.RequiredWithAll | class | Passes when this field is present and non-nil if all of the listed sibling fields are present and non-nil. |
validate.rules.RequiredWithout | class | Passes when this field is present and non-nil if any of the listed sibling fields are absent or nil. |
validate.rules.RequiredWithoutAll | class | Passes when this field is present and non-nil if all of the listed sibling fields are absent or nil. |
validate.rules.Same | class | Passes when the field’s value matches the value of another field in the same data dictionary. |
validate.rules.Size | class | Passes when the value’s size equals n. |
validate.rules.StartsWith | class | Passes when the string starts with one of the given prefixes. |
validate.rules.StringRule | class | Passes only when the value is a string. |
validate.rules.Timezone | class | Passes when the value is a real IANA timezone identifier (e.g. "UTC", "Africa/Lagos",… |
validate.rules.Uppercase | class | Passes when the string value contains only uppercase characters. |
validate.rules.Url | class | Passes when the value is a syntactically valid HTTP or HTTPS URL. |
validate.rules.Uuid | class | Passes when the value is a valid canonical UUID (xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx). |
validate.same | function | The value must match the value of other_field in the same data dictionary. |
validate.schema | function | Creates a new Schema from the given ruleset dictionary. |
validate.size | function | The value’s size must equal exactly n. |
validate.sometimes | function | Skips the entire rule chain when the field is absent from the data dictionary or its value is nil / blank. |
validate.starts_with | function | The string must start with one of the given prefixes. |
validate.string | function | The value must be a string. |
validate.timezone | function | The value must be a valid timezone identifier recognised by the date module (e.g. "UTC",… |
validate.uppercase | function | The string must be entirely uppercase. |
validate.url | function | The value must be a valid HTTP or HTTPS URL. |
validate.use | function | Attaches a pre-defined Rule subclass (the class itself, not an instance) directly. |
validate.uuid | function | The value must be a valid canonical UUID (xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx). |
validate.value | function | Returns a bare Validator with no rules pre-applied. |
Submodules
| Module | Reached as | Summary |
|---|---|---|
validate.rule | validate.rule.* | Defines the base Rule class that all built-in and custom validation rules extend. |
validate.rules | import validate.rules | All built-in validation rule implementations. |
validate.schema | validate.schema.* | |
validate.validator | validate.validator.* | |
validate.validators | validate.* | Top-level convenience functions that create a fresh Validator instance with one rule pre-applied. |
Functions
schema()
validate.schema(ruleset) -> Schema
Creates a new Schema from the given ruleset dictionary.
This is the primary entry point for defining a validation schema. Each
key is a field name (supports dot-notation and .* wildcards) and each
value must be a Validator instance.
import validate
var schema = validate.schema({
username: validate.required().string().alpha_dash().max_length(30),
email: validate.required().string().email(),
})
Parameters
ruleset(dict)
Returns Schema
Raises ArgumentError
Raises ValueError
2026, Richard Ore and The Zuri Contributors