Documentation
¶
Overview ¶
Package validation provides a programmatic, type-safe validation engine using Go generics.
Overview ¶
Validation in Credo uses generic Rule interfaces and pointer-based field references for compile-time type safety. No struct tags are used for rule definition. Reflection is limited to field name extraction (cached after first use per struct type).
Quick Start ¶
import v "github.com/credo-go/credo/validation"
type CreateUserInput struct {
Name string `json:"name"`
Email string `json:"email"`
Age int `json:"age"`
}
func (c *CreateUserInput) Validate() error {
return v.ValidateStruct(c,
v.Field(&c.Name, v.Required[string](), v.Length(2, 100)),
v.Field(&c.Email, v.Required[string](), v.Email()),
v.Field(&c.Age, v.Required[int](), v.Min(18)),
)
}
When the struct implements Validatable, the Validate method is called automatically by BindBody and BindQuery. In debug mode, a warning is logged if the target does not implement Validatable.
Error Format ¶
Validation errors are returned as Errors ([]ValidationError), designed for RFC 7807 Problem Details integration. Each error includes Field, Code, Message, and optional Params for i18n template rendering.
PATCH Support ¶
For partial updates with pointer fields, use NilSafe:
v.Field(&u.Name, v.NilSafe(v.Length(2, 100)))
Adapted From ¶
API design adapted from ozzo-validation (MIT). Generic Rule architecture inspired by govy (design only, no code adapted). See NOTICES file for full attribution.
Maturity: experimental
Index ¶
- func ValidateStruct(structPtr any, fields ...FieldRules) error
- type Errors
- type FieldRules
- type Rule
- func AllowedExtensions(exts ...string) Rule[*multipart.FileHeader]
- func AllowedMimeTypes(types ...string) Rule[*multipart.FileHeader]
- func Between[T cmp.Ordered](min, max T) Rule[T]
- func By[T any](fn func(T) error) Rule[T]
- func DateAfter(threshold time.Time) Rule[time.Time]
- func DateBefore(threshold time.Time) Rule[time.Time]
- func Each[T any](rules ...Rule[T]) Rule[[]T]
- func Email() Rule[string]
- func In[T comparable](values ...T) Rule[T]
- func Length(min, max int) Rule[string]
- func Max[T cmp.Ordered](threshold T) Rule[T]
- func MaxFileSize(maxBytes int64) Rule[*multipart.FileHeader]
- func Min[T cmp.Ordered](threshold T) Rule[T]
- func NilSafe[T any](rules ...Rule[T]) Rule[*T]
- func NotEmptyMap[K comparable, V any]() Rule[map[K]V]
- func NotEmptySlice[E any]() Rule[[]E]
- func NotNil[T any]() Rule[*T]
- func Regex(pattern *regexp.Regexp) Rule[string]
- func Required[T comparable]() Rule[T]
- func URL() Rule[string]
- func UUID() Rule[string]
- func When[T any](condition bool, rules ...Rule[T]) Rule[T]
- type Validatable
- type ValidationError
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func ValidateStruct ¶
func ValidateStruct(structPtr any, fields ...FieldRules) error
ValidateStruct validates a struct by running all field rules. Returns nil if all validations pass, or Errors containing all failures.
structPtr must be a non-nil pointer to a struct. Passing a nil pointer returns nil (valid). Passing a non-pointer or pointer to non-struct panics.
Types ¶
type Errors ¶
type Errors []ValidationError
Errors is a collection of validation errors. It implements the error interface and json.Marshaler for RFC 7807 integration.
func (Errors) MarshalJSON ¶
MarshalJSON implements json.Marshaler. Serializes as a JSON array.
type FieldRules ¶
type FieldRules interface {
// contains filtered or unexported methods
}
FieldRules is a type-erased container returned by Field. The unexported methods prevent external implementation — only Field can produce values satisfying this interface.
func Field ¶
func Field[T any](fieldPtr *T, rules ...Rule[T]) FieldRules
Field creates a FieldRules that validates the struct field pointed to by fieldPtr using the given rules. The type parameter T is inferred from fieldPtr.
When fieldPtr points to a field that implements Validatable and no explicit rules are given, Validate() is called automatically (nested struct support).
type Rule ¶
Rule is the generic validation rule interface. The type parameter T matches the field type, providing compile-time type safety.
func AllowedExtensions ¶
func AllowedExtensions(exts ...string) Rule[*multipart.FileHeader]
AllowedExtensions creates a Rule that restricts the uploaded file's extension (taken from multipart.FileHeader.Filename) to the given list. Extensions are compared case-insensitively, and a leading dot is optional in the allow-list — "jpg" and ".jpg" are equivalent. A file whose name has no extension fails unless "" is explicitly allowed. A nil file header passes — use Required to enforce presence.
func AllowedMimeTypes ¶
func AllowedMimeTypes(types ...string) Rule[*multipart.FileHeader]
AllowedMimeTypes creates a Rule that restricts the uploaded file's declared MIME type to the given list. The MIME type is read from the part's Content-Type header, which is client-supplied and therefore not authoritative — pair it with content sniffing (e.g. http.DetectContentType) when stronger guarantees are needed. Comparison is case-insensitive and ignores media-type parameters such as "; charset=utf-8". A nil file header passes — use Required to enforce presence.
func By ¶
By creates a Rule from an inline function. The type parameter T is inferred from the function signature.
validation.Field(&c.Code, validation.By(func(code string) error {
if len(code) != 2 {
return errors.New("must be a 2-letter code")
}
return nil
}))
func DateAfter ¶
DateAfter creates a Rule that validates the time is after the given threshold. Zero time passes validation — use Required to enforce non-zero.
func DateBefore ¶
DateBefore creates a Rule that validates the time is before the given threshold. Zero time passes validation — use Required to enforce non-zero.
func Each ¶
Each creates a Rule that validates each element of a slice against the given rules. Error fields use bracket notation: "[0]", "[1]", etc.
func Email ¶
Email creates a Rule that validates email format. Empty strings pass validation — use Required to enforce non-empty.
func In ¶
func In[T comparable](values ...T) Rule[T]
In creates a Rule that checks the value is one of the allowed values. The allowed values are not included in the error message for security.
func Length ¶
Length creates a Rule that validates string length (rune count) is between min and max (inclusive). Uses utf8.RuneCountInString for correct Unicode handling. Empty strings pass validation — use Required to enforce non-empty.
func MaxFileSize ¶
func MaxFileSize(maxBytes int64) Rule[*multipart.FileHeader]
MaxFileSize creates a Rule that fails if the uploaded file is larger than maxBytes. The size is read from multipart.FileHeader.Size, which the multipart parser fills in from the request. A nil file header passes — use Required to enforce presence.
func NilSafe ¶
NilSafe wraps rules for use with pointer fields. When the pointer is nil, validation is skipped. When non-nil, the value is dereferenced and inner rules execute. Used for PATCH/partial update support.
type UpdateUserInput struct {
Name *string `json:"name"`
}
validation.Field(&u.Name, validation.NilSafe(validation.Length(2, 100)))
func NotEmptyMap ¶
func NotEmptyMap[K comparable, V any]() Rule[map[K]V]
NotEmptyMap creates a Rule that fails when the map has no entries (nil or zero length). It is the map counterpart of Required, whose comparable constraint excludes maps; the check is len-based — no reflection.
validation.Field(&c.Limits, validation.NotEmptyMap[string, int]())
func NotEmptySlice ¶
NotEmptySlice creates a Rule that fails when the slice has no elements (nil or zero length). It is the slice counterpart of Required, whose comparable constraint excludes slices; the check is len-based — no reflection.
validation.Field(&o.Items, validation.NotEmptySlice[Item]())
func Regex ¶
Regex creates a Rule that validates the value matches the given pattern. The pattern must be pre-compiled via regexp.MustCompile or regexp.Compile. Empty strings pass validation — use Required to enforce non-empty. Panics if pattern is nil — a nil pattern is a programming error, never a runtime condition.
func Required ¶
func Required[T comparable]() Rule[T]
Required creates a Rule that fails if the value is the zero value for type T. Works with any comparable type: strings, ints, bools, etc.
func URL ¶
URL creates a Rule that validates URL format. The URL must have both a scheme and a host. Empty strings pass validation — use Required to enforce non-empty.
func UUID ¶
UUID creates a Rule that validates UUID format. Accepts both hyphenated (xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx) and plain (xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx) formats. Empty strings pass validation — use Required to enforce non-empty.
func When ¶
When creates a Rule that conditionally applies rules based on a boolean condition. When the condition is false, validation is skipped (returns nil). When true, all inner rules are executed and errors are collected.
validation.Field(&o.CardNumber,
validation.When(o.PaymentMethod == "card",
validation.Required[string](), validation.Length(13, 19),
),
)
type Validatable ¶
type Validatable interface {
Validate() error
}
Validatable is implemented by types that can validate themselves. When a target struct implements Validatable, the Bind methods (BindBody, BindQuery) automatically call Validate() after decoding ("parse, don't validate").
Nested struct fields that implement Validatable are auto-validated by Field when no explicit rules are given.
type ValidationError ¶
type ValidationError struct {
// Field is the field path, e.g. "name", "address.city", "items[0]".
Field string `json:"field"`
// Code is the rule identifier / i18n key, e.g. "required", "email", "min".
Code string `json:"code"`
// Message is the default English error message.
Message string `json:"message"`
// Params holds template variables for localized messages,
// e.g. {"min": 2, "max": 100}.
//
// Params is serialized into the HTTP error response (deliberately —
// clients use it to render localized messages). Custom rules must not
// place internal or sensitive values here.
Params map[string]any `json:"params,omitempty"`
}
ValidationError represents a single field validation failure.
func (*ValidationError) Error ¶
func (e *ValidationError) Error() string
Error implements the error interface.