# Validation rule reference

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

Source: https://orbitalhq.com/docs/data-validation/validation-rules

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](/docs/data-validation/data-validation)
for how activation works.

## All rules

| Rule | Parameters | Applies to | Fails when |
|------|------------|------------|------------|
| `@NotEmpty` | — | strings, collections | the string is `""`, or the collection has no items |
| `@MinLength` | `value : Int` | strings | the string is shorter than `value` |
| `@MaxLength` | `value : Int` | strings | the string is longer than `value` |
| `@Pattern` | `regex : String` | strings | the string doesn't fully match `regex` |
| `@Min` | `value : Decimal`, `exclusive : Boolean = false` | numbers | the number is below `value` — or at it, when `exclusive = true` |
| `@Max` | `value : Decimal`, `exclusive : Boolean = false` | numbers | the number is above `value` — or at it, when `exclusive = true` |
| `@MultipleOf` | `value : Decimal` | numbers | the number isn't an exact multiple of `value` |
| `@MinItems` | `value : Int` | collections | the collection has fewer than `value` items |
| `@MaxItems` | `value : Int` | collections | the collection has more than `value` items |
| `@UniqueItems` | — | collections | the collection contains duplicates |
| `@Email` | — | strings | the string isn't a valid email address |
| `@Uuid` | — | strings | the 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:

| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `message` | `String` | the rule's own message | Replaces the generated violation message |
| `severity` | `Severity` | `Error` | One of `Info`, `Warning` or `Error` |

```taxi
import com.orbitalhq.validation.Valid
import com.orbitalhq.validation.MaxLength
import com.orbitalhq.validation.Severity

@Valid
model 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](/docs/data-validation/data-validation#choosing-a-threshold).

## String rules

```taxi
import com.orbitalhq.validation.Valid
import com.orbitalhq.validation.NotEmpty
import com.orbitalhq.validation.MinLength
import com.orbitalhq.validation.MaxLength
import com.orbitalhq.validation.Pattern

@Valid
model 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}")`:

| Value | Result |
|-------|--------|
| `AB1234` | passes |
| `AB1234-extra` | fails — the pattern must match the entire value |
| `hello` | fails |

**Note: A broken regex won't break your query**
If the `regex` won't compile, Orbital disables that rule and reports `Info`-severity
  advice saying the rule wasn't evaluated, rather than failing requests. See
  [when a rule cannot be evaluated](/docs/data-validation/data-validation#when-a-rule-cannot-be-evaluated).

## Numeric rules

```taxi
import com.orbitalhq.validation.Valid
import com.orbitalhq.validation.Min
import com.orbitalhq.validation.Max
import com.orbitalhq.validation.MultipleOf

@Valid
model 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":

| Declaration | `0` | `0.01` |
|-------------|-----|--------|
| `@Min(value = 0)` | passes | passes |
| `@Min(value = 0, exclusive = true)` | fails | passes |

### @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

```taxi
import com.orbitalhq.validation.Valid
import com.orbitalhq.validation.MinItems
import com.orbitalhq.validation.MaxItems
import com.orbitalhq.validation.UniqueItems

type Tag inherits String
type Contributor inherits String
type Category inherits String

@Valid
model 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.

**Note: Collection rules report at the collection**
A rule on a collection reports its violation at the collection's own path — `tags`, not
  `tags[0]`. Rules on the collection's _element type_ report against each element, with
  the index in the path.

## Format rules

```taxi
import com.orbitalhq.validation.Valid
import com.orbitalhq.validation.Email
import com.orbitalhq.validation.Uuid

@Email
type EmailAddress inherits String

@Uuid
type CorrelationId inherits String

@Valid
model 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:

| Rule | Reported when |
|------|---------------|
| `com.orbitalhq.validation.NotNull` | A non-nullable field inside a `@Valid` scope is holding `null` |
| `com.orbitalhq.validation.ParseFailure` | A 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](/docs/data-validation/data-validation#activating-validation-with-valid).

## 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`.
