Skip to content

Core Concepts

A short tour of the primitives that make up govalid. Read this once and the rest of the docs will feel obvious.

The check loop

Calling govalid.Check(v) does the following:

  1. Resolve v to a struct value via reflection. Pointers are dereferenced; nils return (nil, true) instead of panicking.
  2. Walk every exported field, recursing into struct fields and slices-of-structs.
  3. For each field with a valid tag, parse the rule list and execute every checker in order against that field's value.
  4. Collect every *ErrContext returned by a checker.
  5. If the value (or its pointer form) implements Validate() error, call it last and append any non-nil error.
  6. Return (errs, len(errs) == 0).

Tags

govalid reads three struct tags. Their names are configurable through govalid.RulesField, govalid.LabelField and govalid.MessageField in case you need to coexist with another tag namespace.

TagDefaultPurpose
validRule list, ;-separated. Parameters follow :, multiple parameters separated by ,.
labelfield nameHuman-friendly name in error messages. Supports label-<lang> per-locale overrides.
msgOverride the entire error message for the field's first failing rule.

See the Tags Reference for every detail.

Checkers

A checker is a func(CheckerContext) *ErrContext. govalid ships with 17 of them registered in the package-level Checkers map:

go
var Checkers = map[string]CheckFunc{
    "required":     required,
    "min":          min,
    "max":          max,
    "minlen":       minlen,
    "maxlen":       maxlen,
    "alpha":        alpha,
    "alphanumeric": alphaNumeric,
    "alphadash":    alphaDash,
    "username":     userName,
    "email":        email,
    "ipv4":         ipv4,
    "mobile":       mobile,
    "tel":          tel,
    "phone":        phone,
    "idcard":       idCard,
    "equal":        equal,
    "list":         list,
}

You can replace, extend, or remove entries on this map at runtime. Registration must happen before Check is called concurrently — the map itself is not synchronized.

CheckerContext

Each checker receives a fully-populated context. Custom checkers use the same shape as built-ins:

go
type CheckerContext struct {
    StructValue      reflect.Value // the surrounding struct
    FieldName        string        // Go field name
    FieldType        reflect.Type
    FieldValue       interface{}   // the value to inspect
    FieldLabel       string        // resolved label
    TemplateLanguage language.Tag
    Rule             *rule         // includes Rule.params
}

ErrContext

Failures are returned as *ErrContext. It implements error, so each failure can be wrapped, logged, or returned like any other Go error:

go
type ErrContext struct {
    FieldName        string
    FieldLabel       string
    FieldValue       interface{}
    TemplateLanguage language.Tag
    // ... internal template state
}

func (e *ErrContext) Error() string

You usually just iterate over the returned slice:

go
errs, ok := govalid.Check(form)
if !ok {
    for _, e := range errs {
        log.Printf("[%s] %s = %v: %s", e.FieldName, e.FieldLabel, e.FieldValue, e)
    }
}

Templates and locales

Each rule has an associated message template. govalid bundles two locale dictionaries (language.Chinese — the default — and language.English) and resolves them like this:

  1. Locale picked at call site: Check(v, language.English).
  2. Unknown locale → fall back to the default locale.
  3. Unknown rule → fall back to the locale's _unknownErrorTemplate.

Override or augment templates via SetMessageTemplates:

go
govalid.SetMessageTemplates(map[string]string{
    "required": "must not be empty",
}, language.English)

See Internationalization for the full lifecycle.

What required actually means

required is the rule most people get wrong. govalid's definition:

Field kindRequired failure means…
string, slice, array, map, chanlength is 0
pointer, interface, funcvalue is nil
Other comparable kindsvalue equals the zero value
Incomparable struct typesrule is skipped (won't panic)

This matters for bool fields — required on a bool means "must be true". If you want "either value is fine, just present", drop the rule.

Released under the MIT License.