Skip to content

templatedop/govalid

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

19 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

govalid

govalid

Blazing fast, zero-allocation, type-safe validation for Go

Go Version License Build Status Go Report Card


πŸš€ Overview

govalid generates type-safe validation code from struct field markers. No reflection, no runtime overhead, just blazing fast validation.

⚑ Why govalid?

🎯 Performance Benefits

  • Zero allocations: All validation functions perform zero heap allocations
  • 5x to 44x faster: Significantly outperforms reflection-based validators
  • Compile-time optimization: Generated code is optimized by the Go compiler

πŸ‘¨β€πŸ’» Developer Experience

  • Type safety: Validation functions are generated with proper types, eliminating runtime reflection
  • Early error detection: Invalid validation rules are caught during code generation, not at runtime
  • No runtime dependencies: Generated code has minimal external dependencies

πŸ”§ Comprehensive Go Support

  • Full collection support: Maps and channels work with size validators (not supported by most libraries)
  • CEL expressions: Common Expression Language support for complex validation logic
  • Go zero-value semantics: Proper handling of Go's zero values and nil states
  • Unicode-aware: String validators properly handle Unicode characters

πŸ“Š Performance Comparison

Feature govalid Reflection Validators
Performance ~1-14ns, 0 allocs ~50-700ns, 0-5 allocs
Type Safety βœ… Generated functions ❌ Runtime reflection
Collections slice, array, map, channel slice, array only
Dependencies βœ… Minimal ❌ Heavy runtime deps
Error Detection βœ… During code generation ❌ Runtime
CEL Support βœ… Full support ❌ Limited/None

πŸ“¦ Installation

Install the govalid command-line tool by one of supported ways:

Using go install:

Defaults to latest @ and version to install specific release

go install github.com/templatedop/govalid/cmd/govalid

Or:

# Clone the repository
git clone https://github.com/templatedop/govalid.git

# Navigate to the project directory
cd govalid

# Install the tool
go install ./...

Verify the installation:

govalid -h

🎯 Quick Start

1. Define Your Struct

govalid supports two ways to define validation rules:

Using Struct Tags (Recommended)

type Person struct {
    Name  string `json:"name" validate:"required"`
    Email string `json:"email" validate:"email"`
}

Using Comment Markers (Legacy)

type Person struct {
    // +govalid:required
    Name  string `json:"name"`
    // +govalid:email
    Email string `json:"email"`
}

Both approaches work identically. Struct tags are recommended for better integration with existing Go validation libraries.

2. Generate Validation Code

go to root of project

govalid .

This generates validation code like:

// Code generated by govalid; DO NOT EDIT.
import (
	"errors"
	"github.com/templatedop/govalid"
	govaliderrors "github.com/templatedop/govalid/validation/errors"
	"github.com/templatedop/govalid/validation/validationhelper"
)

var (
	// ErrNilPerson is returned when the Person is nil.
	ErrNilPerson = errors.New("input Person is nil")

	// ErrPersonNameRequiredValidation is returned when the Name is required but not provided.
	ErrPersonNameRequiredValidation = govaliderrors.ValidationError{Reason: "field Name is required", Path: "Person.Name", Type: "required"}

	// ErrPersonEmailEmailValidation is the error returned when the field is not a valid email address.
	ErrPersonEmailEmailValidation = govaliderrors.ValidationError{Reason: "field Email must be a valid email address", Path: "Person.Email", Type: "email"}
)

var _ govalid.Validator = (*Person)(nil)

func ValidatePerson(t *Person) error {
	if t == nil {
		return ErrNilPerson
	}

	var errs govaliderrors.ValidationErrors

	if t.Name == "" {
		err := ErrPersonNameRequiredValidation
		err.Value = t.Name
		errs = append(errs, err)
	}

	if !validationhelper.IsValidEmail(t.Email) {
		err := ErrPersonEmailEmailValidation
		err.Value = t.Email
		errs = append(errs, err)
	}

	if len(errs) > 0 {
		return errs
	}
	return nil
}

func (p *Person) Validate() error {
	return ValidatePerson(p)
}

3. Use Generated Validators

func main() {
	p := &Person{Name: "John", Email: "invalid-email"}

	if err := ValidatePerson(p); err != nil {
		log.Printf("Validation failed: %v", err)
		// Output: Validation failed: field Email must be a valid email address
		if errors.Is(err, ErrPersonEmailEmailValidation) {
			log.Printf("Email validation failed, handle error as needed: %v", err)
		}
	}
}

3.1 Handle multiple validation errors

In case of multiple validation errors, govalid generated validators will aggregate all errors and return a list of structs that implement error interface.

func main() {
	p := &Person{Name: "", Email: "invalid-email"}

	if err := ValidatePerson(p); err != nil {
		log.Printf("Validation failed: %v", err)

		if errors.Is(err, ErrPersonEmailEmailValidation) {
			log.Printf("First email error", err)
		}

		if errors.Is(err, ErrPersonNameRequiredValidation) {
			log.Printf("Second required error %v", err)
		}
	}
}

3.2 Validator Interface

func main() {
	p := &Person{Name: "John", Email: "invalid-email"}

	if err := p.Validate(); err != nil {
		log.Printf("Validation failed: %v", err)
	}
}

The generated Validate() method enables seamless integration with HTTP middleware:

import (
	"net/http"
	"github.com/templatedop/govalid/validation/middleware"
)

func CreatePersonHandler(w http.ResponseWriter, r *http.Request) {
	w.Write([]byte("OK"))
}

func main() {
	http.HandleFunc("/person", middleware.ValidateRequest[*Person](CreatePersonHandler))
	http.ListenAndServe(":8080", nil)
}

πŸ”§ Advanced Features

Struct-Level Validation

Apply validation rules to entire structs:

// Using struct tags
type Person struct {
    Name  string `validate:"required"`
    Email string `validate:"required"`
    Age   int    `validate:"required"`
}

// Or using comment markers (legacy)
// +govalid:required
type Person struct {
    Name  string
    Email string
    Age   int
}

CEL Expression Support

Use Common Expression Language for complex validation:

// Using struct tags
type User struct {
    Age           int `validate:"cel=value >= 18 && value <= 120"`
    RetirementAge int `validate:"cel=value >= this.Age"`
}

// Or using comment markers (legacy)
type User struct {
    // +govalid:cel=value >= 18 && value <= 120
    Age int
    // +govalid:cel=value >= this.Age
    RetirementAge int
}

Collection Support

Validate maps, channels, slices, and arrays:

// Using struct tags
type UserList struct {
    Users    []User          `validate:"max=10"`  // slice support
    UserMap  map[string]User `validate:"max=10"`  // map support
    UserChan chan User       `validate:"max=10"`  // channel support
}

// Or using comment markers (legacy)
// +govalid:maxitems=10
type UserList struct {
    Users    []User           // slice support
    UserMap  map[string]User  // map support
    UserChan chan User        // channel support
}

πŸ“ Supported Markers

πŸ“– View Complete Marker Reference

For a complete reference of all supported markers, see MARKERS.md.

Numeric Validators:

  • required - Field must not be zero value
  • gt, gte, lt, lte - Greater than, greater/equal, less than, less/equal
  • min - Minimum value (inclusive)
  • eq - Equals specific value
  • ne - Not equals specific value

String Validators:

  • maxlength, minlength, length - String length validation
  • alpha - Alphabetic characters only
  • alphanum - Alphanumeric characters only
  • lowercase - Must be all lowercase
  • oneof - Value must be one of specified options
  • containsany - Must contain any of specified characters
  • excludes - Must not contain substring
  • excludesall - Must not contain any of specified characters

Collection Validators:

  • maxitems, minitems - Collection size validation
  • unique - All elements must be unique

Format Validators:

  • email, url, uuid - Standard format validation
  • uri - URI format validation
  • fqdn - Fully Qualified Domain Name
  • ipv4, ipv6 - IP address validation
  • latitude, longitude - Geographic coordinate validation
  • iscolour - Color format (hex, rgb, rgba, hsl, hsla, named)
  • number, numeric - Numeric string validation
  • boolean - Boolean string validation

Type Validators:

  • isdefault - Must be zero/default value
  • enum - Enumeration validation

Duration Validators:

  • minduration, maxduration - Time duration constraints

Conditional Validators:

  • required_if, required_unless - Conditional required based on other field value
  • required_with, required_with_all - Required when other fields present
  • required_without, required_without_all - Required when other fields absent
  • excluded_if, excluded_unless - Conditional exclusion based on other field value
  • excluded_with, excluded_with_all - Excluded when other fields present
  • excluded_without, excluded_without_all - Excluded when other fields absent

Advanced:

  • cel - Common Expression Language support for complex expressions
  • date - Date format validation
  • Struct-level markers
  • Cross-field validation

πŸš€ Performance Benchmarks

govalid consistently outperforms reflection-based validators by 5x to 44x:

Validator govalid go-playground Improvement
Required 1.9ns 85.5ns 44.2x
GT/LT 1.9ns 63.0ns 32.5x
MaxLength 15.7ns 73.5ns 4.7x
Email 38.2ns 649.4ns 17.0x

All with 0 allocations vs competitors' 0-5 allocations

πŸ“Š View Complete Benchmarks

πŸ”§ Development Setup

For contributors, install lefthook to enable pre-commit checks:

make install-lefthook

Because of this, lefthook is installed, then the code-base would be checked automatically before each commit, ensuring code quality and consistency.

πŸ“„ License

MIT License - see LICENSE file for details.


Built with ❀️ for the Go community

About

No description, website, or topics provided.

Resources

License

Contributing

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors