Mutual Exclusivity
Mutual Exclusivity Validation — Detailed guide with examples
FraiseQL provides a comprehensive validation system for input constraints. This reference covers all available rule types and their configurations.
FraiseQL’s validation system is uniquely positioned in the GraphQL ecosystem:
Compile-Time Enforcement Unlike Apollo, Strawberry, and TypeGraphQL which validate at runtime, FraiseQL enforces all validation rules during schema compilation. This means invalid input structures are impossible at runtime — errors happen during development, not in production.
13 Built-In Validators Most GraphQL frameworks offer 5-8 validators. FraiseQL includes 13:
Three of these (AnyOf, ConditionalRequired, RequiredIfAbsent) go beyond the GraphQL specification.
Part of a Larger Framework This isn’t just validation — it’s part of FraiseQL’s Phase 4 “batteries-included validation” strategy. Upcoming features include:
Zero Dependencies No external validation libraries needed. Everything is built-in, compiled, and optimized.
Learn more about mutual exclusivity validators in the Mutual Exclusivity guide.
Field must be present and non-null.
[fraiseql.validation]user_name = "required"{ "type": "required"}Error: Field is required
Field value must match a regular expression pattern.
[fraiseql.validation]user_email = { pattern = "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$", message = "Invalid email format"}{ "type": "pattern", "value": { "pattern": "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$", "message": "Invalid email format" }}Parameters:
pattern (string, required): Regular expression patternmessage (string, optional): Custom error messageError: <custom message> or Must match pattern
String field length must be within specified bounds.
[fraiseql.validation]password = { length = { min = 8, max = 128 } }username = { length = { min = 3 } }code = { length = { max = 10 } }{ "type": "length", "value": { "min": 8, "max": 128 }}Parameters:
min (integer, optional): Minimum length (inclusive)max (integer, optional): Maximum length (inclusive)Error:
Length between 8 and 128Length at least 8Length at most 128Numeric field value must be within specified bounds.
[fraiseql.validation]age = { range = { min = 0, max = 150 } }price = { range = { min = 0 } }quantity = { range = { max = 1000 } }{ "type": "range", "value": { "min": 0, "max": 150 }}Parameters:
min (integer, optional): Minimum value (inclusive)max (integer, optional): Maximum value (inclusive)Error:
Value between 0 and 150Value at least 0Value at most 150Field value must be one of allowed values.
[fraiseql.validation]status = { enum = ["draft", "published", "archived"]}{ "type": "enum", "value": { "values": ["draft", "published", "archived"] }}Parameters:
values (array of strings, required): Allowed valuesError: Must be one of: draft, published, archived
Field value must pass checksum validation (e.g., Luhn for credit cards, Mod-97 for IBANs).
[fraiseql.validation]credit_card = { checksum = "luhn" }iban = { checksum = "mod97" }{ "type": "checksum", "value": { "algorithm": "luhn" }}Supported Algorithms:
luhn - Luhn algorithm (credit cards, etc.)mod97 - Mod-97 algorithm (IBANs, ISINs, etc.)Error: Invalid <algorithm>
Compare a field against another field in the same input.
[fraiseql.validation]end_date = { cross_field = { field = "start_date", operator = "gt" } }{ "type": "cross_field", "value": { "field": "start_date", "operator": "gt" }}Parameters:
field (string, required): Name of field to compare againstoperator (string, required): Comparison operatorOperators:
lt - Less thanlte - Less than or equaleq - Equalgte - Greater than or equalgt - Greater thanError: Must be gt start_date
Apply rules only if a condition is met.
[fraiseql.validation]payment_method = { conditional = { condition = "isPremium == true", then_rules = ["required"] }}{ "type": "conditional", "value": { "condition": "isPremium == true", "then_rules": [ { "type": "required" } ] }}Parameters:
condition (string, required): Condition expressionthen_rules (array, required): Rules to apply if condition trueError: Varies by nested rules
Exactly one field from a set must be provided. See Mutual Exclusivity Validation.
[fraiseql.validation]create_post_input = { one_of = ["authorId", "authorPayload"]}{ "type": "one_of", "value": { "fields": ["authorId", "authorPayload"] }}Error: Exactly one of [authorId, authorPayload] must be provided, but 0 were provided
At least one field from a set must be provided. See Mutual Exclusivity Validation.
[fraiseql.validation]contact_input = { any_of = ["email", "phone", "address"]}{ "type": "any_of", "value": { "fields": ["email", "phone", "address"] }}Error: At least one of [email, phone, address] must be provided
If one field is present, others must be too. See Mutual Exclusivity Validation.
[fraiseql.validation]checkout_input = { conditional_required = { if_field_present = "isPremium", then_required = ["paymentMethod", "billingAddress"] }}{ "type": "conditional_required", "value": { "if_field_present": "isPremium", "then_required": ["paymentMethod", "billingAddress"] }}Error: Since 'isPremium' is provided, 'paymentMethod', 'billingAddress' must also be provided
If one field is absent, others must be provided. See Mutual Exclusivity Validation.
[fraiseql.validation]create_order_input = { required_if_absent = { absent_field = "addressId", then_required = ["street", "city", "state", "zip"] }}{ "type": "required_if_absent", "value": { "absent_field": "addressId", "then_required": ["street", "city", "state", "zip"] }}Error: Since 'addressId' is not provided, 'street', 'city', 'state', 'zip' must be provided
All rules in the set must pass (AND logic).
[fraiseql.validation]complex_field = { all = [ { length = { min = 5, max = 50 } }, { pattern = "^[a-z]+$" } ]}{ "type": "all", "value": [ { "type": "length", "value": { "min": 5, "max": 50 } }, { "type": "pattern", "value": { "pattern": "^[a-z]+$" } } ]}Error: Multiple errors returned for each failed rule
At least one rule must pass (OR logic).
[fraiseql.validation]flexible_field = { any = [ { pattern = "^\\d+$" }, # numeric { pattern = "^[a-z]+$" } # alphabetic ]}{ "type": "any", "value": [ { "type": "pattern", "value": { "pattern": "^\\d+$" } }, { "type": "pattern", "value": { "pattern": "^[a-z]+$" } } ]}Error: At least one rule must pass
[fraiseql.validation]
# Basic field validationuser_email = { required = true, pattern = "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$"}
user_password = { required = true, length = { min = 8, max = 128 }, pattern = "^(?=.*[A-Za-z])(?=.*\\d).+$" # letters and numbers}
user_age = { range = { min = 0, max = 150 }}
user_status = { required = true, enum = ["active", "inactive", "suspended"]}
# Mutual exclusivity rulescreate_post_input = { one_of = ["authorId", "authorPayload"]}
contact_info = { any_of = ["email", "phone", "address"]}
# Conditional rulescheckout_input = { conditional_required = { if_field_present = "isPremium", then_required = ["paymentMethod", "billingAddress"] }}
order_location = { required_if_absent = { absent_field = "savedLocationId", then_required = ["latitude", "longitude"] }}[fraiseql.validation]
# Multiple validators on one fieldssn = { required = true, length = { min = 11, max = 11 }, pattern = "^\\d{3}-\\d{2}-\\d{4}$"}
# Credit card with checksumcredit_card = { required = true, length = { min = 13, max = 19 }, checksum = "luhn"}
# Date range validationstart_date = { required = true }end_date = { required = true, cross_field = { field = "start_date", operator = "gt" }}
# Either/or patternaddress = { required_if_absent = { absent_field = "addressId", then_required = ["street", "city", "state", "zip"] }}Evaluation Order:
Performance Characteristics:
All validators are evaluated before SQL execution (fail-fast).
All validation errors are returned with:
Example error response:
{ "errors": [ { "message": "Since 'isPremium' is provided, 'paymentMethod', 'billingAddress' must also be provided", "path": "checkoutInput" } ]}Mutual Exclusivity
Mutual Exclusivity Validation — Detailed guide with examples
TOML Configuration
TOML Configuration — Configuration syntax
Error Handling
Error Handling — Handle validation errors