Rene Nochebuena ab11fd2ace feat(valid)!: promote to v1.0.0 — JSON tag name resolution, bump xerrors to v1.0.0
Resolve JSON tag name resolution roadmap item: field names in error context
now use the json struct tag when available, falling back to the Go field name.
Commits MessageProvider interface as stable. Bumps xerrors dependency from
v0.9.0 to v1.0.0. API committed as stable.
2026-05-11 18:18:05 -06:00

valid

Struct validation backed by go-playground/validator with structured error output.

Module: code.nochebuena.dev/go/valid Tier: 1 — depends on xerrors (Tier 0) and go-playground/validator/v10 Go: 1.25+ Dependencies: code.nochebuena.dev/go/xerrors, github.com/go-playground/validator/v10


Overview

valid wraps go-playground/validator/v10 and maps validation failures to structured *xerrors.Err values. It replaces the old check package, adding a plain constructor, a swappable message provider, and English messages by default.

Installation

go get code.nochebuena.dev/go/valid

Quick start

import "code.nochebuena.dev/go/valid"

type CreateUserRequest struct {
    Name  string `validate:"required"`
    Email string `validate:"required,email"`
    Age   int    `validate:"min=18,max=120"`
}

v := valid.New()

err := v.Struct(req)
if err != nil {
    // err is a *xerrors.Err with code ErrInvalidInput.
    var xe *xerrors.Err
    if errors.As(err, &xe) {
        fmt.Println(xe.Code())        // INVALID_ARGUMENT
        fmt.Println(xe.Message())     // "field 'Email' must be a valid email address"
        fmt.Println(xe.Fields())      // map[field:Email tag:email]
    }
}

Usage

Creating a validator

// English messages (default).
v := valid.New()

// Spanish messages.
v := valid.New(valid.WithMessageProvider(valid.SpanishMessages))

// Custom message provider.
v := valid.New(valid.WithMessageProvider(myProvider))

Validating structs

err := v.Struct(myStruct)
Outcome Error returned
All fields pass nil
Field constraint failure *xerrors.Err with ErrInvalidInput, first error only
Not a struct *xerrors.Err with ErrInternal

The returned *xerrors.Err for field failures carries:

  • Fields()["field"] — the failing struct field name
  • Fields()["tag"] — the failing validation rule (e.g. "email", "required")
  • Unwrap() — the underlying validator.ValidationErrors

Message providers

MessageProvider maps a validation failure to a human-readable message:

type MessageProvider interface {
    Message(field, tag, param string) string
}

Built-in presets:

Variable Language Usage
DefaultMessages English automatic (no option needed)
SpanishMessages Spanish WithMessageProvider(valid.SpanishMessages)

Custom provider example:

type myMessages struct{}

func (m myMessages) Message(field, tag, param string) string {
    switch tag {
    case "required":
        return field + " is mandatory"
    default:
        return field + " failed: " + tag
    }
}

v := valid.New(valid.WithMessageProvider(myMessages{}))

Design decisions

No singletonvalid.New(opts...) returns a plain value. Multiple validators with different configurations can coexist. Tests create isolated instances without global state.

Only the first validation error is surfacedgo-playground/validator returns all field errors at once; we surface only the first for API simplicity. Apps needing all failures can cast errors.Unwrap(err) to validator.ValidationErrors:

var xe *xerrors.Err
errors.As(err, &xe)

var ve validator.ValidationErrors
errors.As(errors.Unwrap(xe), &ve) // all field errors

valid imports xerrors directlyvalid is Tier 1 and xerrors is Tier 0. The import is intentional; valid constructs *xerrors.Err values. Duck-typing is reserved for cases where the import would create a circular or cross-tier dependency.

Spanish is bundled, not a separate module — the Spanish preset is a small, zero-dep addition. Splitting it into a separate module would add publish overhead for negligible gain.

Ecosystem

Tier 0:   xerrors
               ↑ (direct import — constructs *xerrors.Err)
Tier 1:   valid ← you are here
               ↑
Tier 2:   httputil (injects valid.Validator into generic handler)

License

MIT

Description
Struct validator backed by go-playground/validator with xerrors integration and i18n.
Readme MIT 52 KiB
2026-05-11 18:21:29 -06:00
Languages
Go 100%