This example demonstrates how to use go-playground/validator/v10 for input and output validation in gorkflow workflows.
The example shows a user registration workflow with three steps:
- Validate User - Validates registration input and creates a user
- Send Email - Sends a welcome email
- Format Result - Formats the final workflow output
Each step uses struct validation tags to enforce data constraints.
type UserRegistrationInput struct {
Email string `validate:"required,email"`
Username string `validate:"required,min=3,max=20,alphanum"`
Age int `validate:"required,gte=18,lte=120"`
Password string `validate:"required,min=8"`
}- Email: Required, must be valid email format
- Username: Required, 3-20 characters, alphanumeric only
- Age: Required, between 18 and 120
- Password: Required, minimum 8 characters
type ValidatedUserOutput struct {
UserID string `validate:"required,uuid4"`
Email string `validate:"required,email"`
Username string `validate:"required"`
IsActive bool `json:"isActive"`
}- UserID: Required, must be valid UUID v4
- Email: Required, must be valid email
- Username: Required
type EmailSentOutput struct {
MessageID string `validate:"required"`
Status string `validate:"required,oneof=sent failed pending"`
SentTo string `validate:"required,email"`
}- MessageID: Required
- Status: Required, must be one of:
sent,failed,pending - SentTo: Required, must be valid email
step := workflow.NewStep(
"my_step",
"My Step",
handler,
workflow.WithValidation(workflow.DefaultValidationConfig),
)This enables both input and output validation with fail-on-error behavior.
step := workflow.NewStep(
"my_step",
"My Step",
handler,
workflow.WithValidation(workflow.ValidationConfig{
ValidateInput: true,
ValidateOutput: true,
FailOnValidationError: true,
CustomValidator: nil, // Use default validator
}),
)// Validate input only
step := workflow.NewStep(
"my_step",
"My Step",
handler,
workflow.WithInputValidation(),
)
// Validate output only
step := workflow.NewStep(
"my_step",
"My Step",
handler,
workflow.WithOutputValidation(),
)import "github.com/go-playground/validator/v10"
// Create custom validator with custom rules
customValidator := validator.New()
customValidator.RegisterValidation("custom_rule", myCustomValidationFunc)
step := workflow.NewStep(
"my_step",
"My Step",
handler,
workflow.WithCustomValidator(customValidator),
workflow.WithValidation(workflow.DefaultValidationConfig),
)cd example/validation
go run main.goThe example runs 4 test cases:
All validation passes, workflow completes successfully.
Input validation fails with error:
validation failed:
- field 'Email' failed on 'email' tag: got value 'not-an-email'
Input validation fails with error:
validation failed:
- field 'Username' failed on 'min' tag (param: 3): got value 'ab'
Input validation fails with error:
validation failed:
- field 'Age' failed on 'gte' tag (param: 18): got value '16'
When validation fails:
- Input Validation: Fails before the step handler executes
- Output Validation: Fails after the step handler executes
- Error Format: Provides detailed information about which field failed and why
- Workflow Behavior:
- If
FailOnValidationError: true→ Step fails, workflow stops - If
FailOnValidationError: false→ Error logged, execution continues
- If
Here are some commonly used validation tags from go-playground/validator:
required- Field must not be emptyemail- Must be valid email formaturl- Must be valid URLmin=N- Minimum lengthmax=N- Maximum lengthlen=N- Exact lengthalphanum- Alphanumeric characters onlyalpha- Alphabetic characters onlynumeric- Numeric characters onlyoneof=val1 val2- Must be one of the specified values
eq=N- Equal to Nne=N- Not equal to Ngt=N- Greater than Ngte=N- Greater than or equal to Nlt=N- Less than Nlte=N- Less than or equal to N
uuid- Valid UUID (any version)uuid4- Valid UUID v4ip- Valid IP addressipv4- Valid IPv4 addressipv6- Valid IPv6 addressdatetime=layout- Valid datetime with specified layout
eqfield=Field- Equal to another fieldnefield=Field- Not equal to another fieldgtfield=Field- Greater than another fieldltfield=Field- Less than another field
- Early Error Detection: Catch invalid data before processing
- Type Safety: Enforce data contracts between steps
- Self-Documenting: Validation tags serve as documentation
- Consistent Error Messages: Standardized error format
- Reduced Boilerplate: No need to write manual validation code
- Production Ready: Battle-tested validation library