An open API service indexing awesome lists of open source software.

https://github.com/sivchari/govalid

Up to 45x faster 🚀 Auto generate type-safe validation code for structs based on markers.
https://github.com/sivchari/govalid

abstract-syntax-tree autogenerated error-handling go golang translation type-safe validation

Last synced: 4 months ago
JSON representation

Up to 45x faster 🚀 Auto generate type-safe validation code for structs based on markers.

Awesome Lists containing this project

README

          


govalid

govalid


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

![Go Version](https://img.shields.io/badge/Go-1.21+-blue.svg)
![License](https://img.shields.io/badge/license-MIT-green.svg)
![Build Status](https://img.shields.io/badge/build-passing-brightgreen.svg)
[![Go Report Card](https://goreportcard.com/badge/github.com/sivchari/govalid)](https://goreportcard.com/report/github.com/sivchari/govalid)

---

## 🚀 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
```bash
go install github.com/sivchari/govalid/cmd/govalid
```

Or:

```bash
# Clone the repository
git clone https://github.com/sivchari/govalid.git

# Navigate to the project directory
cd govalid

# Install the tool
go install ./...
```

Verify the installation:

```bash
govalid -h
```

## 🎯 Quick Start

### 1. Define Your Struct
```go
// Add validation markers above your struct
// +govalid:required
type Person struct {
Name string `json:"name"`
// +govalid:email
Email string `json:"email"`
}
```

### 2. Generate Validation Code
```bash
govalid generate
```

This generates validation code like:

```go
// Code generated by govalid; DO NOT EDIT.
import (
"errors"
"github.com/sivchari/govalid"
govaliderrors "github.com/sivchari/govalid/validation/errors"
"github.com/sivchari/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

```go
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.

```go
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
```go
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:

```go
import (
"net/http"
"github.com/sivchari/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:

```go
// All fields will be validated as required
// +govalid:required
type Person struct {
Name string
Email string
Age int
}
```

### CEL Expression Support
Use Common Expression Language for complex validation:

```go
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:

```go
// +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](MARKERS.md).

**Core Validators:**
- `required` - Field must not be zero value
- `gt`, `gte`, `lt`, `lte` - Numeric comparisons
- `maxlength`, `minlength` - String length validation
- `maxitems`, `minitems` - Collection size validation
- `enum` - Enumeration validation
- `email`, `url`, `uuid`, `numeric` - Format validation

**Advanced:**
- `cel` - Common Expression Language support
- Struct-level markers
- Custom validation logic

## 🚀 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](test/benchmark/README.md)

### 🔧 Development Setup

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

```bash
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](LICENSE) file for details.

---


Built with ❤️ for the Go community