DEV Community

Cover image for Simplify Validation in Golang— No Struct Tags, No Reflection, TypeSafety: Meet GoValidator
Reza Khademi
Reza Khademi

Posted on

Simplify Validation in Golang— No Struct Tags, No Reflection, TypeSafety: Meet GoValidator

Validation in Go often means wrestling with reflection-heavy libraries or juggling struct tags.

GoValidator takes a fresh, minimalist approach—delivering precise validation without those complications.

What Is GoValidator?

GoValidator is a Go library designed for simplicity, speed, and ease of use.

No struct tags: you don’t need to clutter your code with metadata.

No reflection: that means even faster performance and better type safety.

Lightweight and extendable: perfect for custom validation needs.

Getting Started

Installation:
go get github.com/rezakhademix/govalidator/v2

Importing:
import validator "github.com/rezakhademix/govalidator/v2"

Validation Rules at a Glance

GoValidator provides a wide range of built-in rules (customizable per field): (list of all rules)

  • Basic checks: RequiredInt, RequiredFloat, RequiredString, RequiredSlice
  • Range validations: BetweenInt, BetweenFloat, BetweenString, In
  • Specialized checks: Date, Email, URL, UUID
  • Database checks: Exists, NotExists, ExistExceptSelf
  • Advanced logic: CustomRule, When, RegexMatches, Time, Date

Why Use it?

Simplicity: No boilerplate or tags; validations are explicit and direct.
Extensibility: Define your own rules with the CustomRule() method
Go Packages
Performance: Skipping reflection yields better speed and less overhead. See the benchmark section:

GoValidator benchmark performance compared to reflection-based Go validation libraries.

Example:

Here’s a simple use case for validating a signup form:

v := validator.New()

ok := v.
  RequiredString(username, "username", "username is required").
  Email(email, "email", "email is required").
  BetweenInt(age, 18, 60, "age", "age must be between 18 and 60").
  IsPassed()

if !ok {
  log.Println("validation failed, errors:", v.Errors())
}
Enter fullscreen mode Exit fullscreen mode

With this straightforward API, You define the field, its rule, and custom messages. No tags, no reflection—inline and ensure type safety.

Let dive in for a more complex situation:


type CategoryCreateReq struct {
    ParentID    *int   
    Name        string 
    Description string 
    Status      int   
    Meta        string
}

var req CategoryCreateReq

v := validator.New()

ok := v.
    RequiredString(req.Name, "name", "name of category is required").
    MaxString(req.Name, 500, "name", "name should be less than 500 characters").
    MinString(req.Name, 3, "name", "name should be more than 3 characters").
    NotExists(req.Name, "categories", "name", "name", "name already exists"). // ensure req.Name is unique in DB
    MaxString(req.Description, 1500, "description", "description should be less than 1500 characters").
    MinInt(req.Status, 1, "status", "status can not be less than 1").
    MaxInt(req.Status, 12, "status", "status can not be more than 12").
    IsJSON(req.Meta, "meta","meta should be JSON").
    When(req.ParentID != nil, func() {  
        v.Exists(*req.ParentID, "categories", "id", "parent_id","parent doest not exists")  
    }).
    IsPassed()

if !ok {
    return v.Errors()
}


Enter fullscreen mode Exit fullscreen mode

FAQ

🔹 How do I validate structs in Go without tags?

Use GoValidator’s explicit validation methods (RequiredString, BetweenInt, etc.) instead of struct tags. This keeps your validation logic clear and type-safe.

🔹 Can I define custom validation rules?

Absolutely. You can extend GoValidator with CustomRule() and integrate your own validation logic.

🔹 Does it support database lookups?

Yes. GoValidator includes rules like Exists, NotExists, and ExistExceptSelf for database-backed validation.

Top comments (0)