Getting Started with Govalidator

Before using govalidator, you need to install it in your Go project:

go get github.com/go-playground/validator/v10

This package is part of the go-playground ecosystem and is widely used for data validation in REST APIs, command-line tools, and data processing pipelines.

To start, import the package in your Go file:

import "github.com/go-playground/validator/v10"

Now, you can define a struct and apply validation tags directly to its fields using struct tags.


Struct Validation with Struct Tags

Govalidator supports a wide range of built-in validation rules. Here's an example of a user registration struct with validation constraints:

type User struct {
    Username string `validate:"required,min=3,max=20,alphanum"`
    Email    string `validate:"required,email"`
    Password string `validate:"required,min=8,max=20"`
    Age      int    `validate:"gte=18,lte=99"`
}

To validate a struct, create a new validator instance and call the Struct method:

v := validator.New()
user := User{
    Username: "john_doe",
    Email:    "[email protected]",
    Password: "password123",
    Age:      25,
}

err := v.Struct(user)
if err != nil {
    // Handle validation error
    for _, err := range err.(validator.ValidationErrors) {
        fmt.Printf("Field: %s, Tag: %s, Value: %v\n", err.Field(), err.Tag(), err.Value())
    }
}

This approach is clean and integrates well with frameworks like Echo, Gin, and Fiber.


Custom Validation Rules

Sometimes built-in rules are not enough. Govalidator allows you to define custom validation functions. For example, you might want to ensure a username is not already taken:

func isUsernameAvailable(fl validator.FieldLevel) bool {
    username := fl.Field().String()
    // Simulate a database check
    takenUsernames := []string{"admin", "root", "user"}
    for _, taken := range takenUsernames {
        if username == taken {
            return false
        }
    }
    return true
}

Register the custom rule with the validator:

v := validator.New()
_ = v.RegisterValidation("usernameavailable", isUsernameAvailable)

Then update the struct tag:

type User struct {
    Username string `validate:"required,min=3,max=20,alphanum,usernameavailable"`
    // ... other fields
}

Cross-Field Validation

Govalidator also supports cross-field validation via custom functions. For example, to ensure a password and its confirmation match:

type User struct {
    Password      string `json:"password" validate:"required,min=8"`
    PasswordAgain string `json:"passwordAgain" validate:"required,eqfield=Password"`
}

Here, the eqfield tag ensures that the value of PasswordAgain equals Password.


Best Practices

  1. Use Struct Tags for Simplicity: For simple validation needs, struct tags are more readable and maintainable than inline validation code.
  2. Register Custom Rules Once: Register custom validation functions once at the start of your application to avoid re-registration.
  3. Graceful Error Handling: Always handle validation errors gracefully, especially in HTTP APIs, by returning structured error responses.
  4. Avoid Over-Validation: Only validate necessary fields to avoid unnecessary performance overhead.
  5. Use Tags for Localization: For internationalization, use validation tags that can be mapped to user-facing messages.

Comparison of Validation Features

FeatureBuilt-in SupportCustom SupportStruct TagsCross-Field Validation
String validation
Numeric validation
Struct validation
Custom functions
Field comparison

Real-World Application

In a Go web API, validation is typically applied to request bodies. Consider this example using the Gin framework:

package main

import (
    "github.com/gin-gonic/gin"
    "github.com/go-playground/validator/v10"
    "net/http"
)

type User struct {
    Username string `json:"username" binding:"required,min=3,max=20,alphanum"`
    Email    string `json:"email" binding:"required,email"`
    Password string `json:"password" binding:"required,min=8"`
}

func main() {
    r := gin.Default()

    if v, ok := binding.Validator.Engine().(*validator.Validate); ok {
        _ = v.RegisterValidation("usernameavailable", isUsernameAvailable)
    }

    r.POST("/register", func(c *gin.Context) {
        var user User
        if err := c.ShouldBindJSON(&user); err != nil {
            c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
            return
        }
        c.JSON(http.StatusOK, gin.H{"message": "User registered successfully"})
    })

    r.Run(":8080")
}

This demonstrates how govalidator integrates with Gin's built-in binding and validation system.


Learn more with useful resources