AnnouncementsIntroducing Arkoren

    Menu

    Scalar Rules

    Reject unsuitable text, numbers, booleans, times, and uploaded files without rewriting them.

    What's on this page
    1. Apply text formats, membership rules, and inclusive or strict numeric bounds.
    2. Compare fields and test boolean intent or time windows.
    3. Validate duration text and multipart files while retaining security boundaries.
    On this page

    Rules reject input; they do not repair it. Choose them when an invalid value must produce a diagnostic rather than a silently changed result. The overview establishes construction and execution; examples import arkoren.dev/foundation/validate and use an existing context named ctx.

    Absent scalar values skip ordinary rules. Add Required to reject absence, or use a pointer when an explicit zero must be checked. String, Int, Uint, Float, and Bool preserve named Go types as well as built-in types. Without a callback to infer the type, write validate.String[string]() or validate.Int[int64]().

    Text Constraints

    For a public display name, reject blank or overlong text without changing its spelling:

    name := validate.String(func(text *validate.StringBuilder[string]) {
    	text.Required()
    	text.NotBlank()
    	text.Max(80)
    })
    err := name.Validate(ctx, "Ada Lovelace")

    This passes. Lengths count Unicode runes, not bytes. NotBlank rejects whitespace-only text but does not trim it; normalizers own deliberate transformations.

    Rule Accepted supplied text
    Min, Max, Len Minimum, maximum, or exact nonnegative rune count
    NotBlank At least one non-whitespace character
    OneOf, NotOneOf Membership or nonmembership in the supplied typed values
    Regex Matches a supplied *regexp.Regexp; anchor it to match the whole string
    Prefix, Suffix, Contains Has the supplied prefix, suffix, or substring
    Lowercase, Uppercase Contains no uppercase or no lowercase letters; no rewriting
    Alpha, AlphaNumeric, AlphaDash ASCII letters; letters/digits; or letters/digits/hyphens/underscores
    ASCII ASCII code points only

    For an application plan, plan.OneOf("free", "pro") inside its string callback accepts only those supplied values. There is no separate enum constructor. NotOneOf("admin", "root") excludes reserved names. Negative length bounds record definition errors, not input violations.

    String Formats

    A callback URL should use a scheme your application intends to handle:

    callback := validate.String(func(address *validate.StringBuilder[string]) {
    	address.Required()
    	address.URL("https")
    })
    err := callback.Validate(ctx, "https://example.com/events")

    This passes. URL requires an absolute URL with a host. Its default schemes are HTTP and HTTPS, compared case-insensitively. An explicit list replaces those defaults and is copied when the setting is declared. An empty list selects the defaults; an empty string entry never permits relative or scheme-relative URLs. Syntax validation does not authorize a network destination: apply your own destination policy before fetching an untrusted URL.

    Rule What it checks
    Email Email syntax, not mailbox existence
    URL Absolute URL, host, and allowed scheme
    UUID UUID text
    Domain Domain name, not an entire URL
    IP IPv4 or IPv6 text
    Duration Go duration text accepted by time.ParseDuration

    These checks do not trim whitespace. Add Trim only when accepting and returning cleaned text is intentional.

    Numeric Bounds

    Use integral units when a quantity is naturally a count. A batch size can be bounded without floating-point calculations:

    batch := validate.Int(func(count *validate.IntBuilder[int]) {
    	count.Required()
    	count.Between(1, 100)
    	count.MultipleOf(5)
    })
    err := batch.Validate(ctx, 25)

    This passes. Arguments use the schema’s exact numeric type. Int, Uint, and Float share these assertions:

    Rule Supplied number must be
    Min, Gte Greater than or equal to the bound
    Max, Lte Less than or equal to the bound
    Between Inside both inclusive bounds
    Gt, Lt Strictly greater or strictly less
    Positive, NonNegative Greater than zero or greater than/equal to zero
    MultipleOf An exact multiple of a positive factor
    OneOf, NotOneOf In or outside the listed typed values

    MultipleOf(0) panics immediately. Negative factors and non-finite floating bounds or factors record sticky definition errors. Floating MultipleOf compares exact decimal values from each Go float’s shortest JSON spelling: 0.3 passes a factor of 0.1, without epsilon tolerance. Float32 retains its own encoding precision. This cannot recover precision lost during an earlier calculation.

    Field Comparisons

    To check confirmation input, compare the field with another exact-type field in the nearest enclosing struct:

    type Passwords struct {
    	Password string `json:"password"`
    	Confirm  string `json:"confirm"`
    }
    passwords := validate.Struct(func(object *validate.StructBuilder[Passwords]) {
    	object.Field("Confirm", func(text *validate.StringBuilder[string]) {
    		text.Required()
    		text.EqField("Password")
    	})
    })

    A supplied confirmation must match Password. NeField checks inequality; numeric builders additionally have GtField, GteField, LtField, and LteField. Paths use Go names, not JSON names, and an unreachable target fails the comparison. See Field Paths for nested scope.

    Boolean Intent

    Agreement needs an affirmative value, not merely a non-nil field:

    terms := validate.Bool(func(consent *validate.BoolBuilder[bool]) {
    	consent.Accepted()
    })
    err := terms.Validate(ctx, false)

    This fails: Accepted requires true even though false normally counts as absent. Declined rejects true. To require an explicitly supplied false, require a pointer and apply Declined to its pointee.

    Time Windows

    Time validates time.Time. After and Before are strict; Between includes both bounds. AfterField and BeforeField compare other exact-type time fields. For a future appointment, pin the clock in tests rather than sleeping:

    now := time.Date(2026, time.January, 1, 0, 0, 0, 0, time.UTC)
    fixed := validate.NewClockContext(ctx, func() time.Time {
    	return now
    })
    appointment := validate.Time(func(instant *validate.TimeBuilder) {
    	instant.Required()
    	instant.Future()
    })
    err := appointment.Validate(fixed, now.Add(time.Hour))

    With the standard time package imported, this passes. Equality with the clock fails both Future and Past. A zero instant is absent in every location.

    Duration Values

    If your field remains a string, use String with Duration(). If your decoded type needs a duration value that also passes through JSON, use validate.Duration and Text[validate.Duration]():

    var timeout validate.Duration
    err := timeout.UnmarshalText([]byte("250ms"))
    duration := timeout.Std()
    text, err := timeout.MarshalText()

    On success, duration is a standard time.Duration of 250 milliseconds and text is []byte("250ms"). Failed unmarshaling leaves the receiver unchanged. Raw time.Duration has no representation in encoding/json/v2. The wrapper’s JSONSchemaFormat() returns "go-duration"; standard JSON Schema "duration" uses a different grammar. Text[T] requires text marshaling and unmarshaling on *T; it and Any offer common settings and custom checks, not string-format methods.

    File Uploads

    After multipart decoding, constrain the *multipart.FileHeader itself:

    avatar := validate.File(func(upload *validate.FileBuilder) {
    	upload.Required()
    	upload.Max(2 << 20)
    	upload.Extensions("png")
    	upload.Types("image/png")
    })

    Nil fails Required. Min and Max use the multipart parser’s size in bytes. Extensions ignores case and an optional leading dot. Types opens the file, checks its first 512 bytes using http.DetectContentType, and accepts exact types or wildcards with media-type parameters ignored. Open/read failures are operational errors, not invalid-input violations.

    Content detection is coarse: Office files detect as ZIP, and SVG can contain scripts and is not treated as an image. There is no Image shorthand. Keep antivirus checks, safe storage names, and multipart body limits; these rules do not replace them.

    For every scalar method, see the validate API reference.

    Open a bulk documentation archive?

    This file contains every guide in its scope, not API declarations. Loading it all into an agent can exhaust its context window and increase processing time and cost. For coding tasks, start with the index and read only the relevant pages.

    Use the full archive for offline indexing, bulk processing, or deliberate large-context use.

    Browse the index instead
    Open archive