validator

package
v1.148.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 11, 2026 License: MIT Imports: 12 Imported by: 0

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:

  1. An upstream vt.Validate instance is created and held internally.
  2. Options register field-name tag aliases, custom validation functions, custom type functions, and error message templates on that instance.
  3. 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

func CustomValidationTags() map[string]vt.FuncCtx

CustomValidationTags returns a map of custom tags with validation function.

func ErrorTemplates

func ErrorTemplates() map[string]string

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.

func (*Error) Error

func (e *Error) Error() string

Error returns a string representation of the error.

type Option

type Option func(v *Validator) error

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

func WithCustomValidationTags(t map[string]vt.FuncCtx) Option

WithCustomValidationTags registers custom domain-specific validation rules (e.g., e164noplus, ein, zipcode).

func WithErrorTemplates

func WithErrorTemplates(t map[string]string) Option

WithErrorTemplates registers text/template-based error message translations for validation tags, taking precedence over upstream messages.

func WithFieldNameTag

func WithFieldNameTag(tag string) Option

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

func New(opts ...Option) (*Validator, error)

New constructs a Validator with registered custom tags, field-name aliases, and error templates from the provided options.

func (*Validator) ValidateStruct

func (v *Validator) ValidateStruct(obj any) error

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

func (v *Validator) ValidateStructCtx(ctx context.Context, obj any) error

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.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL