Search documentation

Find a page in the Andurel docs.

Validation

github.com/mbvlabs/andurel/pkg/validation builds structured, JSON-ready field errors and records rule metadata during the same pass. Configuration and model inputs use it without coupling validation to Echo, Fx, Bun, Templ, or Inertia.

Builder lifecycle

Create a builder, add rules, and return Err(). Every rule records its code, message, and parameters even when the value passes. A failed rule also appends a field error.

1func (d CreateProduct) Validate() error {
2    b := validation.NewBuilder()
3    b.Required("Name", d.Name)
4    b.LenBetween("Name", d.Name, 2, 120)
5    b.MinInt("PriceCents", d.PriceCents, 0)
6    b.OneOf("Status", d.Status, "draft", "active", "archived")
7    return b.Err()
8}

Err() returns nil or ValidationErrors. Errors() exposes failures; Rules() returns a defensive copy of client-usable rule metadata. ValidationErrors.ToMap() keeps the first message for each field, matching common form contracts.

Built-in rules

Rule Behavior
Required, RequiredWhen Reject missing or zero-like values, conditionally when needed
MinLen, MaxLen, LenBetween Count Unicode code points in present strings
RecommendedLenBetween Publish recommendation metadata without creating an error
MinInt, MaxInt Bound present integers
OneOf, OneOfWithMessage Restrict a present string to allowed values
URL, RequiredURL Accept HTTP or HTTPS URLs, optionally requiring presence
MinItems, MaxItems, NoBlankItems Validate collection size and blank string elements
TimeBeforeOrEqual Compare two present time values
True Require agreement or confirmation

Most format and range rules ignore absent optional values. Combine them with Required when absence is invalid. Messages may be customized; stable codes and parameters let clients localize or present richer guidance.

Supported values and zero semantics

Presence helpers understand strings, integers, booleans, slices, time.Time, pointers, common database/sql nullable types, and defined aliases whose underlying kind is supported.

Be explicit about zero semantics. Required treats integer zero and zero time as missing, which is correct for many identifiers but wrong when zero is legitimate input. Model optionality with a pointer or nullable type and apply the domain's range rule. NoBlankItems reports indexed fields such as Tags[2]; an empty field name becomes unknown.

Structured errors

Each error carries field, code, message, and optional params:

1{
2  "field": "Name",
3  "code": "min",
4  "message": "must be at least 2 characters",
5  "params": {"min": 2}
6}

validation.As(err) extracts ValidationErrors through wrapped errors. validation.Validate(value) calls Validate() only when a value implements Validatable, which is useful at generic service boundaries.

Domain-specific rules

Use AddField or AddFieldWithParams for failures and publish matching rule metadata when a client needs it:

1b.AddRuleWithParams("Slug", "reserved", "is reserved", map[string]any{
2    "values": reservedSlugs,
3})
4if slices.Contains(reservedSlugs, data.Slug) {
5    b.AddField("Slug", "reserved", "is reserved")
6}

Keep rules deterministic and side-effect free. Database uniqueness, authorization, and race-sensitive invariants belong in services or persistence backed by constraints. Controllers may translate those failures into the same field-error shape.

Templ and Inertia presentation

Templ controllers can pass ValidationErrors to a typed form component and return a 422 full response or fragment patch. Inertia controllers normally call ToMap() and pass it through Page(...).ValidationErrors(...); named error bags follow the request protocol.

Domain validation stays identical across frontend choices. Only transport and presentation differ.

Configuration validation

Generated constructors combine parsing errors with builder errors and semantic checks. The builder covers required values and bounds; constructors cover relationships such as valid ports, supported modes, or timeout ordering. The joined error prevents Fx from starting dependents.

Validation is not authorization. Authenticate the actor, authorize the operation and resource, validate input, then execute it. Never use field validation or hidden request context as a permission system.