v - Data Validation Library
v is a powerful and flexible data validation library, providing a chainable API, rich built-in validation rules, custom error messages, and internationalization support. Supports validation for form data, structs, API parameters, and various other scenarios. Minimum supported Go version: 1.21.0.
Installation
go get -u go-slim.dev/v
Quick Start
package main
import (
"fmt"
"go-slim.dev/v"
)
func main() {
// Simple validation
err := v.Validate(
v.Value("", "username", "Username").Required(),
v.Value("invalid-email", "email", "Email").IsEmail(),
)
if err != nil {
fmt.Println(err)
// Output: Username is required
// Email is not a valid email address
}
}
Core Concepts
Validator
A validator is any type that implements the Validatable interface:
type Validatable interface {
Validate() error
}
Value Validator (Valuer)
Valuer is the core value validator for validating a single value:
// Value(value, field name, label)
validator := v.Value("alice@example.com", "email", "Email Address")
- Value: The actual data to validate
- Field Name: Field identifier (e.g., "email")
- Label: User-readable field name (e.g., "Email Address")
Basic Usage
1. Single Value Validation
import "go-slim.dev/v"
// Required validation
err := v.Value("", "username", "Username").
Required().
Validate()
// Error: Username is required
// Email validation
err := v.Value("alice@example.com", "email", "Email").
Required().
IsEmail().
Validate()
// Pass
// Chained validation
err := v.Value("pass123", "password", "Password").
Required().
MinLength(8).
MaxLength(32).
Validate()
// Error: Password minimum length is 8
2. Multiple Value Validation
Validate - Collect All Errors
Executes all validators and collects all errors:
err := v.Validate(
v.Value("", "username", "Username").Required(),
v.Value("invalid", "email", "Email").IsEmail(),
v.Value("123", "age", "Age").Between(18, 100),
)
if err != nil {
// err contains all validation errors
errs := err.(*v.Errors)
// Get first error
first := errs.First()
fmt.Println(first.String())
// Get errors for specific field
emailErrs := errs.Get("email")
// Convert to map
errMap := errs.ToMap()
// map[string][]*v.Error{
// "username": [...],
// "email": [...],
// }
}
Check - Fail Fast
Returns immediately on first error:
err := v.Check(
v.Value("", "username", "Username").Required(),
v.Value("invalid", "email", "Email").IsEmail(), // Won't execute
)
// Only returns username error
3. Map Data Validation
Validate map data from forms or JSON:
data := map[string]any{
"username": "alice",
"email": "alice@example.com",
"age": 25,
}
field := v.Map(data)
err := v.Validate(
field("username", "Username").Required().MinLength(3),
field("email", "Email").Required().IsEmail(),
field("age", "Age").Required().Between(18, 100),
)
4. Struct Validation
Implement the Validatable interface:
type User struct {
Username string
Email string
Age int
}
func (u *User) Validate() error {
return v.Validate(
v.Value(u.Username, "username", "Username").
Required().
MinLength(3).
MaxLength(20),
v.Value(u.Email, "email", "Email").
Required().
IsEmail(),
v.Value(u.Age, "age", "Age").
Required().
Between(18, 100),
)
}
// Usage
user := &User{Username: "alice", Email: "invalid", Age: 15}
if err := user.Validate(); err != nil {
// Handle validation errors
}
Validation Rules
Required Validation
// Required - Value must be non-empty
v.Value("", "name", "Name").Required()
// RequiredIf - Conditional required
isAdmin := true
v.Value("", "role", "Role").RequiredIf(isAdmin)
// RequiredWith - Depends on other values
phone := ""
email := "alice@example.com"
v.Value(phone, "phone", "Phone").RequiredWith([]any{email})
// If email is not empty, phone is required
String Validation
val := v.Value("hello", "text", "Text")
// Length validation
val.Length(5) // Length equals 5
val.MinLength(3) // Minimum length 3
val.MaxLength(10) // Maximum length 10
val.LengthBetween(3, 10) // Length between 3-10
// Content validation
val.Contains("ell") // Contains substring
val.ContainsAny("aeiou") // Contains any character
val.ContainsRune('h') // Contains specific character
val.Excludes("xyz") // Doesn't contain substring
val.ExcludesAll("123") // Doesn't contain any specified characters
val.ExcludesRune('x') // Doesn't contain specific character
val.StartsWith("he") // Starts with...
val.StartsNotWith("x") // Doesn't start with...
val.EndsWith("lo") // Ends with...
val.EndsNotWith("x") // Doesn't end with...
// Character types
val.IsAlpha() // Only contains letters
val.IsAlphanumeric() // Only contains letters and numbers
val.IsAlphaUnicode() // Only contains letters and Unicode characters
val.IsAlphanumericUnicode() // Only contains letters, numbers, and Unicode
val.IsNumeric() // Numeric string
val.IsAscii() // ASCII characters
val.IsLower() // Lowercase
val.IsUpper() // Uppercase
// Format validation
val.IsHexadecimal() // Hexadecimal
val.IsBase64() // Base64 encoding
val.IsBase64URL() // Base64 URL encoding
val.IsURLEncoded() // URL encoding
val.IsHTML() // HTML content
val.IsHTMLEncoded() // HTML escaping
Numeric Validation
val := v.Value(42, "age", "Age")
// Comparison
val.Equal(42) // Equals
val.NotEqual(0) // Not equals
val.GreaterThan(18) // Greater than
val.GreaterEqualThan(18) // Greater or equal
val.LessThan(100) // Less than
val.LessEqualThan(100) // Less or equal
val.Between(18, 100) // In range
val.NotBetween(0, 17) // Not in range
// Type validation
val.IsNumber() // Number type
val.IsNumeric() // Numeric
val.IsBool() // Boolean
Format Validation
// Network-related
v.Value("alice@example.com", "email", "Email").IsEmail()
v.Value("https://example.com", "url", "URL").IsURL()
v.Value("192.168.1.1", "ip", "IP Address").IsIP()
v.Value("192.168.1.1", "ip", "IP Address").IsIPv4()
v.Value("::1", "ip", "IP Address").IsIPv6()
v.Value("00:11:22:33:44:55", "mac", "MAC Address").IsMAC()
// Phone number
v.Value("+8613800138000", "phone", "Phone").IsE164()
v.Value("13800138000", "phone", "Phone").IsPhoneNumber()
// UUID
v.Value("550e8400-e29b-41d4-a716-446655440000", "id", "ID").IsUUID()
v.Value("...", "id", "ID").IsUUID3()
v.Value("...", "id", "ID").IsUUID4()
v.Value("...", "id", "ID").IsUUID5()
v.Value("01ARZ3NDEKTSV4RRFFQ69G5FAV", "id", "ID").IsULID()
// JWT
v.Value("eyJhbGc...", "token", "Token").IsJwt()
// Hash
v.Value("5d41402abc4b2a76b9719d911017c592", "hash", "Hash").IsMD5()
v.Value("...", "hash", "Hash").IsSHA256()
v.Value("...", "hash", "Hash").IsSHA384()
v.Value("...", "hash", "Hash").IsSHA512()
// Color
v.Value("#FF5733", "color", "Color").IsHexColor()
v.Value("rgb(255, 87, 51)", "color", "Color").IsRgb()
v.Value("rgba(255, 87, 51, 0.5)", "color", "Color").IsRgba()
v.Value("hsl(9, 100%, 60%)", "color", "Color").IsHsl()
v.Value("hsla(9, 100%, 60%, 0.5)", "color", "Color").IsHsla()
v.Value("#FF5733", "color", "Color").IsColor() // Any color format
// Coordinates
v.Value(39.9042, "lat", "Latitude").IsLatitude()
v.Value(116.4074, "lng", "Longitude").IsLongitude()
// Time
v.Value("2024-01-15", "date", "Date").IsDatetime("2006-01-02")
v.Value("Asia/Shanghai", "tz", "Timezone").IsTimezone()
// Other
v.Value("1.2.3", "version", "Version").IsSemver()
v.Value(`{"key":"value"}`, "data", "Data").IsJson()
v.Value("/path/to/file", "path", "Path").IsFile()
v.Value("/path/to/dir", "path", "Path").IsDir()
Enum Validation
// OneOf - Value must be in list
status := "pending"
v.Value(status, "status", "Status").
OneOf([]any{"pending", "active", "inactive"})
// Practical application
role := "admin"
v.Value(role, "role", "Role").
OneOf([]any{"admin", "user", "guest"})
Collection Validation
Every - All Elements Validation
All elements must pass validation:
numbers := []int{1, 2, 3, 4, 5}
err := v.Value(numbers, "numbers", "Number List").
Every(func(item *v.Item) any {
// item.Index: element index
// item.Value: element value
return v.Value(item.Value, "number", "Number").
Between(1, 10)
}).
Validate()
// Map validation
data := map[string]int{
"a": 1,
"b": 2,
"c": 3,
}
err := v.Value(data, "data", "Data").
Every(func(item *v.Item) any {
// item.Key: map key
// item.Value: map value
return v.Value(item.Value, "value", "Value").
GreaterThan(0)
}).
Validate()
Some - Any Element Validation
At least one element must pass validation:
emails := []string{"invalid", "alice@example.com", "bad"}
err := v.Value(emails, "emails", "Email List").
Some(func(item *v.Item) any {
return v.Value(item.Value, "email", "Email").
IsEmail()
}).
Validate()
// Pass (at least one valid email)
Conditional Validation
When - Conditional Execution
Only execute validation when condition is met:
isAdmin := true
err := v.Value("moderator", "role", "Role").
When(isAdmin, func(val *v.Valuer) {
val.OneOf([]any{"admin", "superadmin"})
}).
Validate()
// Only validate role when isAdmin is true
Match - Pattern Matching
Execute different validations based on value:
userType := "email"
identifier := "alice@example.com"
err := v.Value(identifier, "identifier", "Identifier").
Match(func(m *v.Matcher) {
m.Branch("email", func(val *v.Valuer) error {
return val.IsEmail().Validate()
})
m.Branch("phone", func(val *v.Valuer) error {
return val.IsPhoneNumber().Validate()
})
m.Fallback(func(val *v.Valuer) error {
return v.NewError("invalid_type")
})
}).
Validate()
// Practical: validate based on login type
loginType := "phone"
loginValue := "13800138000"
err := v.Value(loginValue, "login", "Login Credential").
Match(func(m *v.Matcher) {
// Login with email
m.Branch("email", func(val *v.Valuer) error {
return val.Required().IsEmail().Validate()
})
// Login with phone
m.Branch("phone", func(val *v.Valuer) error {
return val.Required().IsPhoneNumber().Validate()
})
// Login with username
m.Branch("username", func(val *v.Valuer) error {
return val.Required().MinLength(3).Validate()
})
// Default case
m.Fallback(func(val *v.Valuer) error {
return v.NewError("unsupported_login_type")
})
}).
Validate()
Custom Validation
Custom - Custom Rule
// Return bool
v.Value("hello", "text", "Text").
Custom("custom_check", func(val any) any {
str := val.(string)
return len(str) > 3 // true/false
})
// Return error
v.Value("hello", "text", "Text").
Custom("custom_check", func(val any) any {
str := val.(string)
if len(str) < 3 {
return errors.New("Too short")
}
return nil // or true
})
// Return Validatable
v.Value(user, "user", "User").
Custom("check_user", func(val any) any {
u := val.(*User)
return u // Calls u.Validate()
})
// Practical: check if username already exists
v.Value("alice", "username", "Username").
Custom("username_exists", func(val any) any {
exists := db.UserExists(val.(string))
if exists {
return errors.New("Username already exists")
}
return true
}, v.ErrorFormat("Username {value} is already taken"))
Type Validation
import "reflect"
// Typeof - Validate reflection type
v.Value("hello", "text", "Text").
Typeof(reflect.String)
v.Value(123, "num", "Number").
Typeof(reflect.Int)
// IsString - Convenient string type validation
v.Value("hello", "text", "Text").IsString()
Advanced Features
1. Composite Validators
Every - All Validators Must Pass
validator := v.Every(
v.Value("alice", "username", "Username").Required(),
v.Value("alice@example.com", "email", "Email").IsEmail(),
)
err := validator.Validate()
// All validations must pass
Some - Any Validator Passes
// User can provide either email or phone
validator := v.Some(
v.Value(email, "email", "Email").IsEmail(),
v.Value(phone, "phone", "Phone").IsPhoneNumber(),
)
err := validator.Validate()
// At least one must pass
2. IndexBy - Grouped Validation
For multiple parameter groups, only one complete group is required:
var index int
// User can provide: email+password OR phone+code
err := v.IndexBy(&index, [][]any{
{email, password}, // Group 0
{phone, code}, // Group 1
}).Validate()
if err == nil {
switch index {
case 0:
// Login with email and password
case 1:
// Login with phone and code
}
}
// Practical application
type LoginRequest struct {
Email string
Password string
Phone string
Code string
}
func (r *LoginRequest) Validate() error {
var index int
err := v.Validate(
v.IndexBy(&index, [][]any{
{r.Email, r.Password},
{r.Phone, r.Code},
}),
)
if err != nil {
return err
}
// Execute different validation based on index
switch index {
case 0:
return v.Validate(
v.Value(r.Email, "email", "Email").Required().IsEmail(),
v.Value(r.Password, "password", "Password").Required().MinLength(6),
)
case 1:
return v.Validate(
v.Value(r.Phone, "phone", "Phone").Required().IsPhoneNumber(),
v.Value(r.Code, "code", "Code").Required().Length(6),
)
}
return nil
}
3. Functional Validation
Wrap - Wrap Validation Function
// Create reusable validator
checkUsername := v.Wrap("alice", func(val any) error {
username := val.(string)
if db.UserExists(username) {
return errors.New("Username already exists")
}
return nil
})
err := checkUsername.Validate()
Checker - Function Type
// Checker implements Validatable interface
var passwordCheck v.Checker = func() error {
if password != confirmPassword {
return errors.New("Passwords do not match")
}
return nil
}
err := v.Validate(
v.Value(password, "password", "Password").Required(),
passwordCheck, // Can use directly
)
Error Handling
Error Structure
type Error struct {
// Error code (e.g., "required", "is_email")
Code() string
// Error formatting template
Format() string
// Formatting parameters
Params() map[string]any
// Field name
Field() string
// Field label (user-readable)
Label() string
// Validated value
Value() any
// Internal error
Internal() error
}
Custom Error Messages
// Using ErrorFormat
v.Value("", "username", "Username").
Required(v.ErrorFormat("Please enter {label}"))
// Using ErrorParam to add parameters
v.Value("ab", "username", "Username").
MinLength(3,
v.ErrorFormat("{label} requires at least {min} characters, currently only {length}"),
v.ErrorParam("length", 2),
)
// Using ErrorCode to customize error code
v.Value("", "username", "Username").
Required(v.ErrorCode("custom_required"))
Errors Collection
err := v.Validate(
v.Value("", "username", "Username").Required(),
v.Value("invalid", "email", "Email").IsEmail(),
)
if err != nil {
errs := err.(*v.Errors)
// Check if empty
if !errs.IsEmpty() {
// Get first error
first := errs.First()
fmt.Println(first.String())
// Get all errors
all := errs.All()
// Get errors for specific field
usernameErrs := errs.Get("username")
// Convert to map[field name][]*Error
errMap := errs.ToMap()
for field, fieldErrs := range errMap {
fmt.Printf("%s: %d errors\n", field, len(fieldErrs))
}
// String representation (all errors)
fmt.Println(errs.String())
}
}
Internationalization
Setting Default Translator
import "go-slim.dev/v"
// Custom translation function
v.SetDefaultTranslator(func(message string, params map[string]any) string {
// Load translation from file or database
translated := i18n.Translate(message, params)
// Replace parameter placeholders
for key, value := range params {
translated = strings.ReplaceAll(
translated,
"{"+key+"}",
fmt.Sprintf("%v", value),
)
}
return translated
})
Built-in Chinese Messages
The v package includes built-in Chinese error messages:
// These error codes have Chinese translations
"required" -> "{label} is required"
"is_email" -> "{label} is not a valid email address"
"is_phone_number" -> "{label} is not a valid phone number"
"min_length" -> "{label} minimum length is {min}"
"between" -> "{label} must be greater than or equal to {min} and less than or equal to {max}"
// ... etc.
Use Cases
1. Web Form Validation
import (
"go-slim.dev/slim"
"go-slim.dev/v"
)
type RegisterForm struct {
Username string `json:"username"`
Email string `json:"email"`
Password string `json:"password"`
ConfirmPassword string `json:"confirm_password"`
Age int `json:"age"`
Terms bool `json:"terms"`
}
func (f *RegisterForm) Validate() error {
return v.Validate(
v.Value(f.Username, "username", "Username").
Required().
MinLength(3).
MaxLength(20).
IsAlphanumeric(),
v.Value(f.Email, "email", "Email").
Required().
IsEmail(),
v.Value(f.Password, "password", "Password").
Required().
MinLength(8).
MaxLength(32),
v.Value(f.ConfirmPassword, "confirm_password", "Confirm Password").
Required().
Equal(f.Password,
v.ErrorFormat("Passwords do not match"),
),
v.Value(f.Age, "age", "Age").
Required().
Between(18, 100),
v.Value(f.Terms, "terms", "Terms of Service").
Custom("must_accept", func(val any) any {
return val.(bool) == true
}, v.ErrorFormat("Must accept terms of service")),
)
}
func registerHandler(c slim.Context) error {
var form RegisterForm
if err := c.Bind(&form); err != nil {
return c.JSON(400, map[string]any{"error": "Invalid request data"})
}
if err := form.Validate(); err != nil {
errs := err.(*v.Errors)
return c.JSON(422, map[string]any{
"errors": errs.ToMap(),
})
}
// Create user...
return c.JSON(200, map[string]any{"success": true})
}
2. API Parameter Validation
func updateUserHandler(c slim.Context) error {
data := map[string]any{
"name": c.FormValue("name"),
"email": c.FormValue("email"),
"age": c.FormValue("age"),
}
field := v.Map(data)
err := v.Validate(
field("name", "Name").Required().MinLength(2),
field("email", "Email").Required().IsEmail(),
field("age", "Age").When(data["age"] != "", func(val *v.Valuer) {
val.Between(18, 100)
}),
)
if err != nil {
return c.JSON(422, map[string]any{"errors": err})
}
// Update user...
return c.JSON(200, map[string]any{"success": true})
}
3. Complex Business Validation
type Order struct {
UserID int
Items []OrderItem
PaymentType string
CardNumber string
AlipayID string
TotalAmount float64
}
type OrderItem struct {
ProductID int
Quantity int
Price float64
}
func (o *Order) Validate() error {
return v.Validate(
// User ID must exist
v.Value(o.UserID, "user_id", "User ID").
Required().
Custom("user_exists", func(val any) any {
return db.UserExists(val.(int))
}),
// Order items validation
v.Value(o.Items, "items", "Order Items").
Required().
NotEmpty().
Every(func(item *v.Item) any {
orderItem := item.Value.(OrderItem)
return v.Validate(
v.Value(orderItem.ProductID, "product_id", "Product ID").
Required().
Custom("product_exists", func(val any) any {
return db.ProductExists(val.(int))
}),
v.Value(orderItem.Quantity, "quantity", "Quantity").
Required().
Between(1, 100),
v.Value(orderItem.Price, "price", "Price").
Required().
GreaterThan(0),
)
}),
// Payment method validation
v.Value(o.PaymentType, "payment_type", "Payment Method").
Required().
OneOf([]any{"card", "alipay", "wechat"}).
Match(func(m *v.Matcher) {
// Credit card payment requires card number
m.Branch("card", func(val *v.Valuer) error {
return v.Value(o.CardNumber, "card_number", "Card Number").
Required().
Length(16).
Validate()
})
// Alipay payment requires Alipay account
m.Branch("alipay", func(val *v.Valuer) error {
return v.Value(o.AlipayID, "alipay_id", "Alipay Account").
Required().
Validate()
})
}),
// Total amount validation
v.Value(o.TotalAmount, "total_amount", "Total Amount").
Required().
Custom("amount_match", func(val any) any {
calculated := 0.0
for _, item := range o.Items {
calculated += item.Price * float64(item.Quantity)
}
// Allow 0.01 error margin
diff := calculated - o.TotalAmount
return diff < 0.01 && diff > -0.01
}, v.ErrorFormat("Total amount doesn't match order items")),
)
}
4. Multi-Scenario Validation
type User struct {
ID int
Username string
Email string
Password string
Role string
}
// Validation for creation
func (u *User) ValidateCreate() error {
return v.Validate(
v.Value(u.Username, "username", "Username").
Required().
MinLength(3).
Custom("username_unique", func(val any) any {
return !db.UsernameExists(val.(string))
}),
v.Value(u.Email, "email", "Email").
Required().
IsEmail().
Custom("email_unique", func(val any) any {
return !db.EmailExists(val.(string))
}),
v.Value(u.Password, "password", "Password").
Required().
MinLength(8),
v.Value(u.Role, "role", "Role").
Required().
OneOf([]any{"user", "admin"}),
)
}
// Validation for update
func (u *User) ValidateUpdate() error {
return v.Validate(
v.Value(u.ID, "id", "User ID").
Required().
Custom("user_exists", func(val any) any {
return db.UserExists(val.(int))
}),
v.Value(u.Username, "username", "Username").
When(u.Username != "", func(val *v.Valuer) {
val.MinLength(3)
}),
v.Value(u.Email, "email", "Email").
When(u.Email != "", func(val *v.Valuer) {
val.IsEmail()
}),
// Password not required for update
v.Value(u.Password, "password", "Password").
When(u.Password != "", func(val *v.Valuer) {
val.MinLength(8)
}),
)
}
5. Nested Struct Validation
type Address struct {
Street string
City string
ZipCode string
}
func (a *Address) Validate() error {
return v.Validate(
v.Value(a.Street, "street", "Street").Required(),
v.Value(a.City, "city", "City").Required(),
v.Value(a.ZipCode, "zip_code", "Zip Code").
Required().
Length(6).
IsNumeric(),
)
}
type Profile struct {
Name string
Age int
Address Address
}
func (p *Profile) Validate() error {
return v.Validate(
v.Value(p.Name, "name", "Name").Required(),
v.Value(p.Age, "age", "Age").Between(18, 100),
// Nested validation
&p.Address, // Implements Validatable interface
)
}
API Reference
Core Functions
// Validation execution
func Validate(validations ...Validatable) error
func Check(validations ...Validatable) error
// Value validator
func Value(value any, field, label string) *Valuer
// Map validator
func Map(data map[string]any) func(name, label string) *Valuer
// Composite validation
func Every(validators ...Validatable) Checker
func Some(validators ...Validatable) Checker
func IndexBy(index *int, values [][]any, options ...ErrorOption) Checker
// Wrap function
func Wrap(value any, check func(any) error) Checker
// Error creation
func NewError(code string, options ...ErrorOption) *Error
// Error options
func ErrorFormat(format string) ErrorOption
func ErrorParam(key string, value any) ErrorOption
func ErrorCode(code string) ErrorOption
// Internationalization
func SetDefaultTranslator(translator Translator)
func DefaultTranslator() Translator
Valuer Methods
// Required
func (v *Valuer) Required(options ...ErrorOption) *Valuer
func (v *Valuer) RequiredIf(condition bool, options ...ErrorOption) *Valuer
func (v *Valuer) RequiredWith(values []any, options ...ErrorOption) *Valuer
// String
func (v *Valuer) Length(n int, options ...ErrorOption) *Valuer
func (v *Valuer) MinLength(min int, options ...ErrorOption) *Valuer
func (v *Valuer) MaxLength(max int, options ...ErrorOption) *Valuer
func (v *Valuer) LengthBetween(min, max int, options ...ErrorOption) *Valuer
func (v *Valuer) Contains(substr string, options ...ErrorOption) *Valuer
func (v *Valuer) StartsWith(prefix string, options ...ErrorOption) *Valuer
func (v *Valuer) EndsWith(suffix string, options ...ErrorOption) *Valuer
// Numeric
func (v *Valuer) Equal(another any, options ...ErrorOption) *Valuer
func (v *Valuer) Between(min, max any, options ...ErrorOption) *Valuer
func (v *Valuer) GreaterThan(min any, options ...ErrorOption) *Valuer
func (v *Valuer) LessThan(max any, options ...ErrorOption) *Valuer
// Format
func (v *Valuer) IsEmail(options ...ErrorOption) *Valuer
func (v *Valuer) IsPhoneNumber(options ...ErrorOption) *Valuer
func (v *Valuer) IsURL(options ...ErrorOption) *Valuer
func (v *Valuer) IsIP(options ...ErrorOption) *Valuer
func (v *Valuer) IsUUID(options ...ErrorOption) *Valuer
// Collection
func (v *Valuer) Every(handle func(item *Item) any, options ...ErrorOption) *Valuer
func (v *Valuer) Some(handle func(item *Item) any, options ...ErrorOption) *Valuer
// Conditional
func (v *Valuer) When(condition bool, then func(*Valuer)) *Valuer
func (v *Valuer) Match(handle func(m *Matcher)) *Valuer
// Custom
func (v *Valuer) Custom(code string, check func(val any) any, options ...ErrorOption) *Valuer
// Execute
func (v *Valuer) Validate() error
Notes
-
Empty Value Handling:
- Non-
Requiredfields skip subsequent validation when empty - Use
NotEmptyto validate non-empty but not required cases
- Non-
-
Error Collection:
Validatecollects all errorsCheckreturns immediately on first error- Choose appropriate approach to balance performance and user experience
-
Custom Validation Return Values:
trueornil: Validation passesfalse: Validation fails, use default error messageerror: Validation fails, use custom error messageValidatable: Calls itsValidate()method
-
Performance Considerations:
- Custom validations (like database queries) may be slow
- Consider caching validation results in business layer
- Use
Everycarefully with large amounts of data
-
Error Message Placeholders:
{label}: Field label{value}: Field value{field}: Field name- Custom parameters: Add via
ErrorParam
-
Chained Calls:
- All validation methods return
*Valuer, supporting chaining - Finally call
Validate()to execute validation - Or pass as parameter to
v.Validate()
- All validation methods return
-
Type Safety:
- Custom validations require manual type assertion
- Mind handling panics from type assertions
- Use
Typeofto pre-validate types
Related Links
- GitHub Repository
- go-slim.dev/is - Underlying validation function library
- Validator Library Comparison