Core Guides
Stable v1.4.8
Request Validation Guide
Learn about Request Validation Guide in EchoNext.
Request Validation Guide
Learn how to validate HTTP requests in EchoNext using struct tags.
Table of Contents
- Basics
- Common Validation Tags
- String Validation
- Numeric Validation
- Array and Slice Validation
- Nested Struct Validation
- Custom Validation
- Error Messages
- Best Practices
Basics
EchoNext uses go-playground/validator for validation.
Simple Example
type CreateUserRequest struct {
Name string `json:"name" validate:"required,min=2,max=100"`
Email string `json:"email" validate:"required,email"`
Age int `json:"age" validate:"required,min=18,max=120"`
}
func createUser(c echo.Context, req CreateUserRequest) (UserResponse, error) {
// req is already validated!
// If validation fails, client gets 422 with error details
user := service.Create(req)
return user, nil
}How It Works
- Client sends JSON request
- EchoNext binds JSON to struct
- EchoNext validates using
validatetags - If validation fails, returns 422 with errors
- If validation passes, calls your handler
Common Validation Tags
Required Fields
type Request struct {
Name string `validate:"required"` // Must be provided
Age int `validate:"required"` // Must be provided
}Optional Fields
type Request struct {
Name string `validate:"required"` // Required
Email string `validate:"omitempty,email"` // Optional, but if provided must be email
Phone string `json:"phone"` // Optional, no validation
}Multiple Constraints
type Request struct {
// Multiple constraints separated by commas
Username string `validate:"required,min=3,max=20,alphanum"`
Password string `validate:"required,min=8,containsany=!@#$%"`
}String Validation
Length
type Request struct {
ShortCode string `validate:"len=6"` // Exactly 6 characters
Username string `validate:"min=3,max=20"` // Between 3 and 20
Password string `validate:"min=8"` // At least 8
Description string `validate:"max=500"` // At most 500
}Format
type Request struct {
Email string `validate:"email"` // Email format
URL string `validate:"url"` // URL format
UUID string `validate:"uuid"` // UUID format
IP string `validate:"ip"` // IP address
IPv4 string `validate:"ipv4"` // IPv4 address
IPv6 string `validate:"ipv6"` // IPv6 address
MAC string `validate:"mac"` // MAC address
Hex string `validate:"hexadecimal"` // Hexadecimal
Base64 string `validate:"base64"` // Base64 encoded
ISBN string `validate:"isbn"` // ISBN number
CreditCard string `validate:"credit_card"` // Credit card number
}Content
type Request struct {
Alpha string `validate:"alpha"` // Only letters
Alphanum string `validate:"alphanum"` // Letters and numbers
Numeric string `validate:"numeric"` // Only numbers
Lowercase string `validate:"lowercase"` // Only lowercase
Uppercase string `validate:"uppercase"` // Only uppercase
// Contains specific characters
Password string `validate:"containsany=!@#$%"`
// Starts/ends with
Code string `validate:"startswith=ABC"`
File string `validate:"endswith=.pdf"`
}Allowed Values
type Request struct {
Status string `validate:"oneof=active inactive pending"`
Role string `validate:"oneof=admin user guest"`
Type string `validate:"oneof=type1 type2 type3"`
}Numeric Validation
Range
type Request struct {
Age int `validate:"min=18,max=120"` // Between 18 and 120
Rating float64 `validate:"min=0,max=5"` // Between 0.0 and 5.0
Quantity int `validate:"min=1"` // At least 1
Discount int `validate:"max=100"` // At most 100
}Comparison
type Request struct {
Price float64 `validate:"gt=0"` // Greater than 0
Stock int `validate:"gte=0"` // Greater than or equal to 0
Discount int `validate:"lt=100"` // Less than 100
MaxUsers int `validate:"lte=1000"` // Less than or equal to 1000
}Special
type Request struct {
Port int `validate:"min=1,max=65535"` // Valid port number
Percent int `validate:"min=0,max=100"` // Percentage
Positive int `validate:"gt=0"` // Positive number
}Array and Slice Validation
Array Length
type Request struct {
Tags []string `validate:"min=1,max=5"` // Between 1 and 5 items
Categories []string `validate:"required,min=1"` // At least 1 item
Options []string `validate:"max=10"` // At most 10 items
}Element Validation
Use dive to validate array elements:
type Request struct {
// Each tag must be 2-20 characters
Tags []string `validate:"required,min=1,max=5,dive,min=2,max=20"`
// Each email must be valid
Emails []string `validate:"dive,email"`
// Each number must be positive
Numbers []int `validate:"dive,gt=0"`
}Nested Arrays
type Request struct {
// Array of arrays
Matrix [][]int `validate:"dive,dive,min=0,max=100"`
}Nested Struct Validation
Simple Nesting
type Address struct {
Street string `validate:"required"`
City string `validate:"required"`
ZipCode string `validate:"required,numeric,len=5"`
}
type CreateUserRequest struct {
Name string `validate:"required"`
Email string `validate:"required,email"`
Address Address `validate:"required"` // Validates nested struct
}Optional Nesting
type Request struct {
// Address is optional, but if provided must be valid
Address *Address `validate:"omitempty"`
}Array of Structs
type Item struct {
Name string `validate:"required"`
Quantity int `validate:"required,min=1"`
Price float64 `validate:"required,gt=0"`
}
type OrderRequest struct {
Items []Item `validate:"required,min=1,dive"`
}Custom Validation
Register Custom Validator
import "github.com/go-playground/validator/v10"
// Custom validation function
func validatePhoneNumber(fl validator.FieldLevel) bool {
phone := fl.Field().String()
// Your validation logic
return len(phone) >= 10 && len(phone) <= 15
}
// Register in main.go
func main() {
app := echonext.New()
// Get validator instance
validate := validator.New()
// Register custom validation
validate.RegisterValidation("phone", validatePhoneNumber)
// Set validator
app.Validator = validate
}
// Use in struct
type Request struct {
Phone string `validate:"required,phone"`
}Cross-Field Validation
Validate one field against another:
type Request struct {
Password string `validate:"required,min=8"`
ConfirmPassword string `validate:"required,eqfield=Password"`
StartDate time.Time `validate:"required"`
EndDate time.Time `validate:"required,gtfield=StartDate"`
MinPrice float64 `validate:"required"`
MaxPrice float64 `validate:"required,gtefield=MinPrice"`
}Error Messages
Default Error Format
When validation fails, EchoNext returns:
{
"success": false,
"data": null,
"error": "Validation failed: Name: min, Email: email, Age: min"
}Custom Error Messages
import (
"github.com/go-playground/validator/v10"
"strings"
)
func customErrorMessage(err error) string {
var messages []string
for _, err := range err.(validator.ValidationErrors) {
field := err.Field()
tag := err.Tag()
var message string
switch tag {
case "required":
message = field + " is required"
case "email":
message = field + " must be a valid email"
case "min":
message = field + " must be at least " + err.Param() + " characters"
case "max":
message = field + " must be at most " + err.Param() + " characters"
default:
message = field + " is invalid"
}
messages = append(messages, message)
}
return strings.Join(messages, ", ")
}Validation Examples
User Registration
type RegisterRequest struct {
Username string `json:"username" validate:"required,min=3,max=20,alphanum"`
Email string `json:"email" validate:"required,email"`
Password string `json:"password" validate:"required,min=8,containsany=!@#$%"`
Age int `json:"age" validate:"required,min=18,max=120"`
Terms bool `json:"terms" validate:"required,eq=true"`
}Product Creation
type CreateProductRequest struct {
Name string `json:"name" validate:"required,min=3,max=200"`
Description string `json:"description" validate:"required,min=10,max=1000"`
Price float64 `json:"price" validate:"required,gt=0"`
SKU string `json:"sku" validate:"required,alphanum,len=8"`
Stock int `json:"stock" validate:"required,gte=0"`
Categories []string `json:"categories" validate:"required,min=1,max=5,dive,min=2,max=50"`
Tags []string `json:"tags" validate:"max=10,dive,min=2,max=30"`
Active bool `json:"active" validate:"required"`
}Search/Filter Request
type SearchRequest struct {
Query string `query:"q" validate:"omitempty,min=2,max=100"`
Page int `query:"page" validate:"min=1"`
Limit int `query:"limit" validate:"min=1,max=100"`
Sort string `query:"sort" validate:"omitempty,oneof=name price created_at"`
Order string `query:"order" validate:"omitempty,oneof=asc desc"`
Status string `query:"status" validate:"omitempty,oneof=active inactive all"`
}Date Range Request
type ReportRequest struct {
StartDate time.Time `json:"start_date" validate:"required"`
EndDate time.Time `json:"end_date" validate:"required,gtfield=StartDate"`
Type string `json:"type" validate:"required,oneof=daily weekly monthly"`
}Address Validation
type AddressRequest struct {
Street string `json:"street" validate:"required,min=5,max=200"`
City string `json:"city" validate:"required,min=2,max=100"`
State string `json:"state" validate:"required,len=2,uppercase"`
ZipCode string `json:"zip_code" validate:"required,numeric,len=5"`
Country string `json:"country" validate:"required,len=2,uppercase"`
IsDefault bool `json:"is_default"`
}Best Practices
1. Be Specific
// ❌ Too generic
type Request struct {
Data string `validate:"required"`
}
// ✅ Specific constraints
type Request struct {
Email string `validate:"required,email"`
Phone string `validate:"required,e164"`
}2. Use Appropriate Types
// ❌ String when number is expected
type Request struct {
Age string `validate:"required"`
}
// ✅ Use correct type
type Request struct {
Age int `validate:"required,min=18,max=120"`
}3. Make Optional Fields Clear
// ❌ Unclear if optional
type Request struct {
Phone string `validate:"e164"`
}
// ✅ Explicitly optional
type Request struct {
Phone string `validate:"omitempty,e164"`
}4. Group Related Validations
type Request struct {
// User identity
Username string `validate:"required,min=3,max=20,alphanum"`
Email string `validate:"required,email"`
// Security
Password string `validate:"required,min=8,containsany=!@#$%"`
// Profile
Age int `validate:"required,min=18,max=120"`
Bio string `validate:"omitempty,max=500"`
}5. Document Complex Validations
type Request struct {
// SKU must be exactly 8 alphanumeric characters
SKU string `validate:"required,alphanum,len=8"`
// Price must be positive and up to 2 decimal places
Price float64 `validate:"required,gt=0"`
}Complete Validation Reference
Common tags:
required- Field must be providedomitempty- Skip validation if emptymin=n- Minimum length/valuemax=n- Maximum length/valuelen=n- Exact lengtheq=value- Equal to valuene=value- Not equal to valuegt=n- Greater thangte=n- Greater than or equallt=n- Less thanlte=n- Less than or equaloneof=a b c- One of listed valuesemail- Email formaturl- URL formatuuid- UUID formatalpha- Alphabetic onlyalphanum- Alphanumeric onlynumeric- Numeric stringdive- Validate array/slice elements
See the full list in the validator documentation.