validation

package
v1.0.1 Latest Latest
Warning

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

Go to latest
Published: Dec 27, 2025 License: MIT Imports: 4 Imported by: 0

README

validation Package

Request validation using struct tags with go-playground/validator. Integrates seamlessly with LaResto's error handling.

Features

  • Struct Tag Validation: Declarative validation rules
  • 30+ Built-in Validators: Email, URL, min/max length, numeric ranges, etc.
  • Custom Validators: Phone numbers, strong passwords (LaResto-specific)
  • Error Integration: Returns errors.ErrValidation with detailed field errors
  • Human-Readable Messages: Automatic error message formatting

Installation

import "github.com/LaRestoOU/laresto-go-common/pkg/validation"

Quick Start

Define Request Structs
type LoginRequest struct {
    Email    string `json:"email" validate:"required,email"`
    Password string `json:"password" validate:"required,min=8"`
}

type RegisterRequest struct {
    Email           string `json:"email" validate:"required,email"`
    Password        string `json:"password" validate:"required,strong_password"`
    ConfirmPassword string `json:"confirm_password" validate:"required,eqfield=Password"`
    FirstName       string `json:"first_name" validate:"required,min=2,max=50"`
    Age             int    `json:"age" validate:"required,gte=18,lte=120"`
    Phone           string `json:"phone" validate:"required,phone"`
}
Validate Requests
v := validation.New()

req := LoginRequest{
    Email:    "user@example.com",
    Password: "password123",
}

if err := v.Validate(req); err != nil {
    // err is *errors.AppError with validation details
    return err
}

Common Validation Tags

String Validators
type Example struct {
    Required  string `validate:"required"`              // Must not be empty
    Email     string `validate:"email"`                 // Valid email format
    URL       string `validate:"url"`                   // Valid URL
    UUID      string `validate:"uuid"`                  // Valid UUID
    Alpha     string `validate:"alpha"`                 // Letters only
    Alphanum  string `validate:"alphanum"`              // Letters and numbers
    Numeric   string `validate:"numeric"`               // Numbers only
    Lowercase string `validate:"lowercase"`             // Lowercase only
    Uppercase string `validate:"uppercase"`             // Uppercase only
}
Length Validators
type Example struct {
    MinLen    string `validate:"min=5"`                 // At least 5 chars
    MaxLen    string `validate:"max=100"`               // At most 100 chars
    ExactLen  string `validate:"len=8"`                 // Exactly 8 chars
    BetweenLen string `validate:"min=5,max=10"`         // Between 5-10 chars
}
Numeric Validators
type Example struct {
    GreaterThan int     `validate:"gt=0"`               // Greater than 0
    GreaterOrEq int     `validate:"gte=18"`             // Greater or equal 18
    LessThan    int     `validate:"lt=100"`             // Less than 100
    LessOrEq    int     `validate:"lte=120"`            // Less or equal 120
    Range       int     `validate:"gte=1,lte=10"`       // Between 1-10
    Positive    float64 `validate:"gt=0"`               // Positive number
}
Comparison Validators
type Example struct {
    Equal       string `validate:"eq=admin"`            // Equals "admin"
    NotEqual    string `validate:"ne=guest"`            // Not equal "guest"
    OneOf       string `validate:"oneof=red green blue"` // One of listed values
    EqualField  string `validate:"eqfield=Password"`    // Equals another field
}
Special Validators
type Example struct {
    Contains    string `validate:"contains=@"`          // Contains substring
    StartsWith  string `validate:"startswith=https://"`// Starts with prefix
    EndsWith    string `validate:"endswith=.com"`      // Ends with suffix
    JSON        string `validate:"json"`                // Valid JSON
    Base64      string `validate:"base64"`              // Valid base64
    Hexadecimal string `validate:"hexadecimal"`         // Valid hex
}

Custom LaResto Validators

Phone Number
type Request struct {
    Phone string `validate:"required,phone"`
}

// Valid formats:
// +1234567890
// +1 (234) 567-8900
// +44 20 1234 5678
Strong Password
type Request struct {
    Password string `validate:"required,strong_password"`
}

// Requirements:
// - At least 8 characters
// - At least one uppercase letter
// - At least one lowercase letter
// - At least one digit

Usage with Gin

Handler Example
func (h *Handler) Register(c *gin.Context) {
    var req RegisterRequest
    if err := c.ShouldBindJSON(&req); err != nil {
        c.JSON(400, gin.H{"error": "Invalid JSON"})
        return
    }

    // Validate request
    if err := h.validator.Validate(req); err != nil {
        // err is *errors.AppError with validation details
        appErr := err.(*errors.AppError)
        c.JSON(appErr.Status, gin.H{
            "error": gin.H{
                "code":    appErr.Code,
                "message": appErr.Message,
                "details": appErr.Details,
            },
        })
        return
    }

    // Process valid request
    user, err := h.service.Register(req)
    // ...
}
Error Response Format

When validation fails, the response looks like:

{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Request validation failed",
    "details": {
      "validation_errors": [
        {
          "field": "Email",
          "message": "Email must be a valid email address",
          "tag": "email",
          "value": "invalid-email"
        },
        {
          "field": "Password",
          "message": "Password must be at least 8 characters",
          "tag": "min",
          "value": "short"
        }
      ]
    }
  }
}

Validating Single Variables

v := validation.New()

// Validate a single value
email := "user@example.com"
if err := v.ValidateVar(email, "required,email"); err != nil {
    // Handle error
}

// Validate with multiple rules
age := 25
if err := v.ValidateVar(age, "required,gte=18,lte=120"); err != nil {
    // Handle error
}

Creating Custom Validators

v := validation.New()

// Register custom validator
v.RegisterValidation("is_even", func(fl validator.FieldLevel) bool {
    value, ok := fl.Field().Interface().(int)
    if !ok {
        return false
    }
    return value%2 == 0
})

// Use in struct
type Example struct {
    Number int `validate:"required,is_even"`
}

Validation Tag Reference

Complete Tag List
Tag Description Example
required Field must not be empty validate:"required"
email Valid email address validate:"email"
url Valid URL validate:"url"
uuid Valid UUID validate:"uuid"
min Minimum length/value validate:"min=5"
max Maximum length/value validate:"max=100"
len Exact length validate:"len=10"
eq Equal to value validate:"eq=admin"
ne Not equal to value validate:"ne=guest"
gt Greater than validate:"gt=0"
gte Greater or equal validate:"gte=18"
lt Less than validate:"lt=100"
lte Less or equal validate:"lte=120"
eqfield Equal to another field validate:"eqfield=Password"
nefield Not equal to another field validate:"nefield=OldPassword"
oneof One of listed values validate:"oneof=red green blue"
contains Contains substring validate:"contains=@"
startswith Starts with prefix validate:"startswith=https://"
endswith Ends with suffix validate:"endswith=.com"
alpha Letters only validate:"alpha"
alphanum Letters and numbers validate:"alphanum"
numeric Numbers only validate:"numeric"
number Valid number validate:"number"
json Valid JSON validate:"json"
jwt Valid JWT token validate:"jwt"
latitude Valid latitude validate:"latitude"
longitude Valid longitude validate:"longitude"
phone Valid phone (custom) validate:"phone"
strong_password Strong password (custom) validate:"strong_password"

Best Practices

DO ✅
// Use clear, descriptive validation tags
type UserRequest struct {
    Email    string `validate:"required,email"`
    Age      int    `validate:"required,gte=18,lte=120"`
    Username string `validate:"required,min=3,max=20,alphanum"`
}

// Combine multiple rules
Password string `validate:"required,min=8,max=128,strong_password"`

// Use eqfield for password confirmation
ConfirmPassword string `validate:"required,eqfield=Password"`

// Validate enums with oneof
Role string `validate:"required,oneof=admin user guest"`
DON'T ❌
// Don't skip validation
var req LoginRequest
c.ShouldBindJSON(&req)
// MISSING: if err := v.Validate(req); err != nil { ... }

// Don't use weak validation
Password string `validate:"required"` // Too weak!

// Don't forget required on important fields
Email string `validate:"email"` // Missing required!

// Don't ignore validation errors
v.Validate(req) // Ignoring error!

Testing

func TestValidation(t *testing.T) {
    v := validation.New()

    // Test valid data
    valid := LoginRequest{
        Email:    "user@example.com",
        Password: "password123",
    }
    if err := v.Validate(valid); err != nil {
        t.Errorf("Expected valid data to pass: %v", err)
    }

    // Test invalid data
    invalid := LoginRequest{
        Email:    "not-an-email",
        Password: "short",
    }
    if err := v.Validate(invalid); err == nil {
        t.Error("Expected invalid data to fail")
    }
}

iOS Developer Notes

Validation is similar to:

  • SwiftUI's @Published property wrappers with validation
  • Combine's validation operators
  • Manual validation in view controllers

Comparison:

// Swift validation (manual)
func validate() -> [String] {
    var errors: [String] = []
    if email.isEmpty {
        errors.append("Email is required")
    }
    if !email.contains("@") {
        errors.append("Email must be valid")
    }
    return errors
}

// Go validation (declarative)
type Request struct {
    Email string `validate:"required,email"`
}

Key concepts:

  • Struct tags = Like Swift property wrappers
  • Validation rules = Declarative, not imperative
  • Error details = Like ValidationError arrays in iOS

Performance

  • Fast: Reflection is cached, validation is optimized
  • No allocations: Most validations don't allocate memory
  • Parallel-safe: Validator can be used concurrently

Benchmarks (from go-playground/validator):

  • ~1-2µs per struct with 10 fields
  • Caching eliminates reflection overhead
  • Thread-safe for concurrent use

API Reference

Types
type Validator struct {
    // Wraps go-playground/validator
}

type ValidationError struct {
    Field   string // Field name that failed
    Message string // Human-readable error message
    Tag     string // Validation tag that failed
    Value   string // Value that failed validation
}
Functions
// New creates a new Validator
func New() *Validator
Methods
// Validate validates a struct
func (v *Validator) Validate(s interface{}) error

// ValidateVar validates a single variable
func (v *Validator) ValidateVar(field interface{}, tag string) error

// RegisterValidation adds a custom validator
func (v *Validator) RegisterValidation(tag string, fn validator.Func) error

License

MIT License - see LICENSE file for details

Documentation

Overview

Package validation provides request validation using struct tags. It wraps go-playground/validator with LaResto-specific error formatting.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type ValidationError

type ValidationError struct {
	Field   string `json:"field"`
	Message string `json:"message"`
	Tag     string `json:"tag"`
	Value   string `json:"value,omitempty"`
}

ValidationError represents a single field validation error.

type Validator

type Validator struct {
	// contains filtered or unexported fields
}

Validator wraps go-playground/validator with custom error formatting.

func New

func New() *Validator

New creates a new Validator instance.

func (*Validator) RegisterValidation

func (v *Validator) RegisterValidation(tag string, fn validator.Func) error

RegisterValidation adds a custom validation function.

func (*Validator) Validate

func (v *Validator) Validate(s interface{}) error

Validate validates a struct and returns a formatted error. Returns nil if validation passes. Returns errors.ErrValidation with details if validation fails.

func (*Validator) ValidateVar

func (v *Validator) ValidateVar(field interface{}, tag string) error

ValidateVar validates a single variable.

Jump to

Keyboard shortcuts

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