Documentation
¶
Overview ¶
Package validator solves two recurring problems with struct validation in Go services: the gap in domain-specific validation rules missing from general libraries, and the cost of turning raw validation errors into user-readable messages. It wraps https://github.com/go-playground/validator — the de-facto standard validation library — and layers a curated set of custom rules, a template-based error translation engine, and a clean functional-options API on top.
Problem ¶
go-playground/validator is comprehensive but generic. Production services typically need a handful of domain rules (E.164 phone numbers, US EINs, ZIP codes, RFC-3339 dates) that are not in the upstream library, and they need validation errors that read as human sentences rather than raw tag names. Wiring those two concerns together for every service is boilerplate this package eliminates.
How It Works ¶
New creates a Validator using a variadic list of Option values:
- An upstream vt.Validate instance is created and held internally.
- Options register field-name tag aliases, custom validation functions, custom type functions, and error message templates on that instance.
- Validator.ValidateStruct (or its context-aware twin Validator.ValidateStructCtx) runs the upstream validator and transforms any vt.ValidationErrors into an errors.Join aggregate of typed Error values, each carrying the failed tag, parameter, namespace, field name, kind, and the translated human-readable message.
Tag groups joined by "|" are iterated individually so each OR-branch failure produces an independent, meaningful error.
Key Features ¶
- Human-readable errors: WithErrorTemplates maps any validation tag to a text/template string. Templates receive an Error value, giving access to the namespace, field name, tag, parameter, kind, and actual value. ErrorTemplates returns a ready-to-use map covering the built-in go-playground/validator tags (for the supported upstream version) plus the custom tags below.
- Custom domain rules: CustomValidationTags registers seven production-ready validators not in the upstream library:
- falseif — conditional negation combinator
- e164noplus — E.164 phone number without the leading '+'
- zipcode — US ZIP code (12345 or 12345-6789)
- usstate — two-letter US state code (including DC)
- usterritory — two-letter US territory code (AS, GU, MP, PR, VI)
- datetime_rfc3339 — strict RFC-3339 datetime
- datetime_rfc3339_relaxed — RFC-3339 with space instead of 'T'
- Field-name aliasing: WithFieldNameTag (commonly set to "json") makes error namespaces use the serialized field names that API consumers actually see, not the internal Go struct field names.
- Extensible: WithCustomValidationTags and WithCustomTypeFunc expose the full upstream registration API for project-specific rules.
- Structured errors: every failure is an Error struct, not a plain string, enabling programmatic error categorisation and localisation downstream.
Usage ¶
v, err := validator.New(
validator.WithFieldNameTag("json"),
validator.WithCustomValidationTags(validator.CustomValidationTags()),
validator.WithErrorTemplates(validator.ErrorTemplates()),
)
if err != nil {
return err
}
type Address struct {
Phone string `json:"phone" validate:"required,e164noplus"`
ZIP string `json:"zip" validate:"required,zipcode"`
State string `json:"state" validate:"required,usstate"`
}
err = v.ValidateStruct(Address{Phone: "12345", ZIP: "bad", State: "XX"})
// err is an errors.Join aggregate containing one *Error per failed field,
// each with a message like:
// "phone must be a valid E.164 formatted phone number without the leading '+' symbol"
Index ¶
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func CustomValidationTags ¶
CustomValidationTags returns a map of custom tags with validation function.
func ErrorTemplates ¶
ErrorTemplates returns a map of validation tags with error messages as text templates.
Types ¶
type Error ¶
type Error struct {
// Tag is the validation tag that failed (e.g. "max").
Tag string
// Param is the Tag's parameter value (if any - e.g. "10").
Param string
// FullTag is the validation tag that failed with included parameters (e.g. "max=10").
FullTag string
// Namespace for the field error, with the tag name taking precedence over the field's actual name.
Namespace string
// StructNamespace is the namespace for the field error, with the field's actual name.
StructNamespace string
// Field is the field name with the tag name taking precedence over the field's actual name.
Field string
// StructField is the field's actual name from the struct.
StructField string
// Kind is the Field's string representation of the kind (e.g. Int,Slice,...).
Kind string
// Value is the actual field's value. It holds the raw input and is exposed
// to error templates, so avoid echoing it into messages for sensitive
// fields (passwords, tokens) to prevent leaking secrets into logs or responses.
Value any
// Err is the translated error message.
Err string
}
Error is a custom error adding a Field member.
type Option ¶
Option is the interface that allows to set configuration options.
func WithCustomTypeFunc ¶
func WithCustomTypeFunc(fn vt.CustomTypeFunc, types ...any) Option
WithCustomTypeFunc registers a custom type converter for specialized types before validation.
func WithCustomValidationTags ¶
WithCustomValidationTags registers custom domain-specific validation rules (e.g., e164noplus, ein, zipcode).
func WithErrorTemplates ¶
WithErrorTemplates registers text/template-based error message translations for validation tags, taking precedence over upstream messages.
func WithFieldNameTag ¶
WithFieldNameTag configures field lookup to use the given struct tag (e.g., "json") instead of field names.
type Validator ¶
type Validator struct {
// contains filtered or unexported fields
}
Validator contains the validator object fields.
func New ¶
New constructs a Validator with registered custom tags, field-name aliases, and error templates from the provided options.
func (*Validator) ValidateStruct ¶
ValidateStruct validates struct fields against registered rules, returning an errors.Join aggregate of structured Errors if any fail.
Example ¶
package main
import (
"fmt"
"log"
"github.com/tecnickcom/nurago/pkg/validator"
)
const (
// fieldTagName is the name of the tag used for the validator rules.
fieldTagName = "json"
)
// SubStruct is an example structure type used to test nested structures.
type SubStruct struct {
URLField string `json:"sub_string" validate:"required,url"`
IntField int `json:"sub_int" validate:"required,min=2"`
}
// RootStruct is an example structure type.
type RootStruct struct {
BoolField bool `json:"bool_field"`
SubStr SubStruct `json:"sub_struct" validate:"required"`
SubStrPtr *SubStruct `json:"sub_struct_ptr" validate:"required"`
StringField string `json:"string_field" validate:"required"`
NoNameField string `json:"-" validate:"required"`
}
func main() {
// data structure to check
validObj := RootStruct{
BoolField: true,
SubStr: SubStruct{
URLField: "http://first.test.invalid",
IntField: 3,
},
SubStrPtr: &SubStruct{
URLField: "http://second.test.invalid",
IntField: 123,
},
StringField: "hello world",
NoNameField: "test",
}
// instantiate the validator object
v, err := validator.New(
validator.WithFieldNameTag(fieldTagName),
validator.WithCustomValidationTags(validator.CustomValidationTags()),
validator.WithErrorTemplates(validator.ErrorTemplates()),
)
if err != nil {
log.Fatal(err)
}
// check the data structure
err = v.ValidateStruct(validObj)
if err != nil {
log.Fatal(err)
}
fmt.Println("OK")
}
Output: OK
func (*Validator) ValidateStructCtx ¶
ValidateStructCtx validates struct fields, threading ctx through to any context-aware custom validators, and returns an errors.Join aggregate of structured Errors if any fail. The built-in custom validators do not inspect ctx, so cancellation only affects user-supplied context-aware rules.