Skip to main content

Validation

VEF uses go-playground/validator as the base validation engine and layers framework-specific behavior on top:

  • translated error messages
  • label / label_i18n support
  • custom validation rules
  • pointer-based nullable field support through the standard omitempty flow

API Reference

APIContract
validator.Validate(value)Runs the package-level validator against value; success returns nil, validation failure returns the first translated error as a bad-request result.Error
validator.RegisterValidationRules(rules...)Registers each ValidationRule with the shared validator and both built-in translators; returns the first registration error
validator.RegisterTypeFunc(fn, types...)Registers a custom type extractor by delegating to RegisterCustomTypeFunc
validator.CustomTypeFuncAlias-compatible function type func(field reflect.Value) any for custom type extraction
validator.ValidationRuleStruct used to define custom validation tags and translation behavior
ValidationRule.RuleTagValidator tag name passed to RegisterValidation and RegisterTranslation
ValidationRule.ErrMessageTemplateFallback message template registered with go-playground translators
ValidationRule.ErrMessageI18nKeyOptional framework i18n key; if it resolves to a real message, that message wins over the fallback template
ValidationRule.ValidateActual rule predicate over go-playground FieldLevel values
ValidationRule.ParseParamExtracts placeholder values from go-playground FieldError values for message rendering
ValidationRule.CallValidationEvenIfNullPassed through to go-playground RegisterValidation

Validation Entry Point

Typed params and typed meta structs are validated automatically after decoding. The framework calls:

validator.Validate(value)

That means handler parameters using typed request structs get validation by default without manual calls inside the handler.

Standard Validator Tags

VEF inherits the standard go-playground/validator tag set. Common examples include:

TagMeaningExample
requiredfield must be present and non-zerovalidate:"required"
emailfield must be a valid emailvalidate:"required,email"
minnumeric or length lower boundvalidate:"min=1"
maxnumeric or length upper boundvalidate:"max=32"
oneoffield must match one of several valuesvalidate:"oneof=admin user guest"
lenexact lengthvalidate:"len=32"
omitemptyskip later validations when emptyvalidate:"omitempty,email"
divevalidate each slice or map itemvalidate:"required,dive"

VEF does not redefine those upstream rules; it adds framework-specific rules on top of them.

Label Resolution

Validation errors use field labels instead of raw Go field names when possible.

Resolution order:

SourceEffect
label:"..."uses the explicit label text directly
label_i18n:"..."resolves the label through i18n.T; if the translation is missing, the i18n key may appear in the error message
neither tag presentuses the Go field name

Example:

type UserParams struct {
api.P

Username string `json:"username" validate:"required" label:"Username"`
Phone string `json:"phone" validate:"phone_number" label_i18n:"user_phone"`
}

Built-In Custom Rules

VEF currently ships these custom validation rules:

Rule tagExpected field typeMeaningExample
phone_numberstringvalidates a Mainland China mobile numbervalidate:"phone_number"
dec_min=<value>decimal.Decimaldecimal value must be greater than or equal to the thresholdvalidate:"dec_min=0"
dec_max=<value>decimal.Decimaldecimal value must be less than or equal to the thresholdvalidate:"dec_max=999.99"
alphanum_usstringletters, numbers, underscore onlyvalidate:"alphanum_us"
alphanum_us_slashstringletters, numbers, underscore, slash onlyvalidate:"alphanum_us_slash"
alphanum_us_dotstringletters, numbers, underscore, dot onlyvalidate:"alphanum_us_dot"

These rules are registered during package initialization. If that registration fails, startup panics instead of leaving the validator partially configured.

phone_number

RuleDetails
accepted values^1[3-9]\d{9}$
intended usemobile phone input validation
common error shapetranslated message meaning “format is invalid”

dec_min / dec_max

RuleDetails
accepted field typedecimal.Decimal
parameter formatdecimal string such as 10.5
behaviorcompares the field value against the parsed decimal threshold
failure modesnon-decimal.Decimal fields or invalid threshold params fail validation

alphanum_us

RuleAllowed characters
alphanum_usletters, digits, _
alphanum_us_slashletters, digits, _, /
alphanum_us_dotletters, digits, _, .

All three alphanum rules require at least one character; an empty string does not match their regex.

Typical uses:

RuleTypical use
alphanum_usaction names, identifiers, simple codes
alphanum_us_slashRPC resource names or slash-separated identifiers
alphanum_us_dotfile names, module names, dotted identifiers

Nullable / Optional Fields

VEF uses pointer types for nullable fields (the older null.* wrapper package was removed in v0.21 in favor of pointers).

type UserParams struct {
// Required: validation runs against the value directly
Username string `json:"username" validate:"required,min=3"`

// Optional: validation only runs when the pointer is non-nil
Phone *string `json:"phone" validate:"omitempty,phone_number"`

// Optional with explicit "may be empty" semantics
Bio *string `json:"bio" validate:"omitempty,max=500"`
}

The standard omitempty validator tag handles nullable cases — when the pointer is nil, subsequent rules are skipped; when it points to a value, the rest of the tag chain applies to that value. If you need to register a custom type that should participate in validation, use validator.RegisterTypeFunc(...) with a validator.CustomTypeFunc to teach the validator how to extract a comparable value from your type.

Error Behavior

Validation returns the first translated validation error as a framework result.Error.

CaseOutcome
validation succeedshandler continues normally
one or more validation rules failframework returns a bad-request style error
non-validation error occurs during validationframework wraps it as a bad-request style error

HTTP behavior:

PropertyValue
business coderesult.ErrCodeBadRequest
HTTP status400 Bad Request

Built-in go-playground rules and VEF custom rules read the active i18n.CurrentLanguage() at validation time. Chinese (zh-CN) uses the Chinese translator; other supported languages use the English translator.

Registering Additional Custom Rules

Applications can register their own rules through validator.RegisterValidationRules(...).

A rule is defined by validator.ValidationRule, which includes:

FieldPurpose
RuleTagvalidator tag name
ErrMessageTemplatefallback translated message template
ErrMessageI18nKeyi18n message key
Validateactual validation function
ParseParamparameter extraction for error message placeholders
CallValidationEvenIfNullwhether the rule should run on null values

For custom rule messages, ErrMessageI18nKey is checked first. When i18n.T(key) returns a value different from the key, placeholders such as {0} and {1} are replaced with ParseParam values. If no framework i18n message is found, the go-playground translator renders ErrMessageTemplate.

Practical Patterns

Simple required fields

type UserParams struct {
api.P

Username string `json:"username" validate:"required,alphanum,max=32" label:"Username"`
Email string `json:"email" validate:"omitempty,email,max=128" label:"Email"`
}

Decimal range validation

type PriceParams struct {
api.P

Amount decimal.Decimal `json:"amount" validate:"dec_min=0,dec_max=999999.99" label:"Amount"`
}

Optional pointer validation

type UserParams struct {
api.P

Phone *string `json:"phone" validate:"omitempty,phone_number" label:"Phone"`
}

Custom Validation Rules

Struct-tag validation above (built-in tags plus the framework's own phone_number / dec_min / dec_max / alphanum_us* rules) and custom rule registration are two views of the same package, not two separate systems: a validate:"..." tag selects which rule runs on a field, and validator.RegisterValidationRules is how a new tag becomes available to select. validator.Validate runs built-in and custom-registered rules together through one call — there is no separate code path for "custom" validation.

APIPurpose
validator.Validate(value)validates a value and returns the first framework validation error
validator.RegisterValidationRules(rules...)adds custom ValidationRule entries
validator.RegisterTypeFunc(fn, types...)registers custom type extraction for application-specific wrappers
validator.CustomTypeFunccallback type accepted by RegisterTypeFunc
validator.ValidationRulecustom rule definition with tag, messages, validator callback, parameter parser, and null-call flag

See Registering Additional Custom Rules above for the full ValidationRule field reference, message-resolution order, and a worked example.

Practical Advice

  • put validation rules on typed params and meta structs, not inside handlers
  • use label or label_i18n so error messages remain user-facing
  • prefer framework custom rules when they match your contract
  • keep custom rules narrow and domain-specific
  • use pointer fields plus omitempty for nullable input; register a custom type extractor only for application-specific wrapper types

Next Step

Read Parameters And Metadata to see where validation is triggered in request decoding.