Data validation

Validation rule reference

The built-in validation rules, their parameters, and what makes them fail

Orbital ships with a set of validation rules in the com.orbitalhq.validation namespace. They’re built in — there’s nothing to add to your project to use them.

Remember that declaring a rule isn’t enough on its own: a rule only runs when a @Valid scope covers it (or when it’s declared directly on a query parameter). See validating data for how activation works.

All rules

RuleParametersApplies toFails when
@NotEmptystrings, collectionsthe string is "", or the collection has no items
@MinLengthvalue : Intstringsthe string is shorter than value
@MaxLengthvalue : Intstringsthe string is longer than value
@Patternregex : Stringstringsthe string doesn’t fully match regex
@Minvalue : Decimal, exclusive : Boolean = falsenumbersthe number is below value — or at it, when exclusive = true
@Maxvalue : Decimal, exclusive : Boolean = falsenumbersthe number is above value — or at it, when exclusive = true
@MultipleOfvalue : Decimalnumbersthe number isn’t an exact multiple of value
@MinItemsvalue : Intcollectionsthe collection has fewer than value items
@MaxItemsvalue : Intcollectionsthe collection has more than value items
@UniqueItemscollectionsthe collection contains duplicates
@Emailstringsthe string isn’t a valid email address
@Uuidstringsthe string isn’t a valid UUID

Parameters every rule accepts

Every rule inherits from com.orbitalhq.validation.ValidationRule, which gives all of them two extra parameters:

ParameterTypeDefaultDescription
messageStringthe rule’s own messageReplaces the generated violation message
severitySeverityErrorOne of Info, Warning or Error
import com.orbitalhq.validation.Validimport com.orbitalhq.validation.MaxLengthimport com.orbitalhq.validation.Severity
@Validmodel Doc {   @MaxLength(value = 3, severity = Severity.Warning, message = "too long")   code : String}

Severity decides whether a violation is serious enough to fail the query. With the default threshold = Error, only Error violations reject — see choosing a threshold.

String rules

import com.orbitalhq.validation.Validimport com.orbitalhq.validation.NotEmptyimport com.orbitalhq.validation.MinLengthimport com.orbitalhq.validation.MaxLengthimport com.orbitalhq.validation.Pattern
@Validmodel Registration {   @NotEmpty username : Username inherits String   @MinLength(value = 8) password : Password inherits String   @MaxLength(value = 140) bio : Bio inherits String   @Pattern(regex = "[A-Z]{2}[0-9]{4}") reference : Reference inherits String}

@NotEmpty

Fails on an empty string, "". A whitespace-only string like " " passes — if you want to catch that, use @Pattern.

@NotEmpty also works on collections, where it fails an empty collection.

@MinLength / @MaxLength

Character-count bounds. Both are inclusive: with @MinLength(value = 3), a 3-character string passes, and a 2-character string fails with:

Value 'ab' is shorter than the minimum length of 3 (was 2)

@Pattern

Matches the whole string against a regular expression — a partial match fails. Given @Pattern(regex = "[A-Z]{2}[0-9]{4}"):

ValueResult
AB1234passes
AB1234-extrafails — the pattern must match the entire value
hellofails

Numeric rules

import com.orbitalhq.validation.Validimport com.orbitalhq.validation.Minimport com.orbitalhq.validation.Maximport com.orbitalhq.validation.MultipleOf
@Validmodel Order {   @Min(value = 1) quantity : Quantity inherits Int   @Min(value = 0, exclusive = true) price : Price inherits Decimal   @Max(value = 100) discountPercent : DiscountPercent inherits Decimal   @MultipleOf(value = 0.01) amount : Amount inherits Decimal}

@Min / @Max

Bounds are inclusive by default — a value exactly on the bound passes. Set exclusive = true to make the bound itself fail, which is how you express “must be greater than zero”:

Declaration00.01
@Min(value = 0)passespasses
@Min(value = 0, exclusive = true)failspasses

@MultipleOf

Comparison is exact decimal arithmetic, not floating point, so 0.3 is a multiple of 0.1. This makes it reliable for money — @MultipleOf(value = 0.01) means “no fractional pennies”.

A value of 0 never produces a violation.

Collection rules

import com.orbitalhq.validation.Validimport com.orbitalhq.validation.MinItemsimport com.orbitalhq.validation.MaxItemsimport com.orbitalhq.validation.UniqueItems
type Tag inherits Stringtype Contributor inherits Stringtype Category inherits String
@Validmodel Post {   @MinItems(value = 2) tags : Tag[]   @MaxItems(value = 10) contributors : Contributor[]   @UniqueItems categories : Category[]}

@MinItems / @MaxItems

Item-count bounds, both inclusive. A collection of exactly value items passes either rule.

@UniqueItems

Fails when a collection contains duplicates. It works on collections of scalars and of models — duplicate models are detected by comparing their values, so two structurally identical objects count as a duplicate.

Format rules

import com.orbitalhq.validation.Validimport com.orbitalhq.validation.Emailimport com.orbitalhq.validation.Uuid
@Emailtype EmailAddress inherits String
@Uuidtype CorrelationId inherits String
@Validmodel Contact {   email : EmailAddress   correlationId : CorrelationId}

Format rules are a natural fit for declaring on the type rather than the field. EmailAddress is an email address wherever it appears, so declaring the rule once means every model that uses the type gets it.

@Email

Checks the string is a plausible email address: a local part, a single @, a domain containing at least one dot, and no whitespace.

@Uuid

Checks the string is a canonical UUID — 8-4-4-4-12 hexadecimal characters separated by hyphens.

Rules Orbital produces for you

Two rule names appear in violations without you ever declaring them. You’ll see these in the rule field of a validation failure report:

RuleReported when
com.orbitalhq.validation.NotNullA non-nullable field inside a @Valid scope is holding null
com.orbitalhq.validation.ParseFailureA value inside a @Valid scope couldn’t be parsed into its declared type

NotNull is why you never declare a “required” rule — your taxonomy already says whether a field is nullable, and @Valid enforces it. See activating validation.

What does not produce a violation

Rules are deliberately narrow about what they apply to, so that a rule declared on a widely-reused type doesn’t cause noise everywhere it’s used:

  • A rule that doesn’t apply to the value’s kind is skipped. @MinLength on a number, or @MinItems on a string, produces nothing — not a violation.
  • null never trips a rule. Rules describe what a value must look like when it’s present. Whether the value is allowed to be absent at all is a nullability question, handled by NotNull.