2024-06-21 21:54:44 +01:00
|
|
|
# Go Validate
|
|
|
|
|
|
2026-09-07 14:01:03 +01:00
|
|
|
Small, composable value validators for Go. A validator is just a
|
|
|
|
|
`func(T) error` — it returns `nil` or an error. You compose them with
|
|
|
|
|
`All` or `Collect` and drop in plain closures wherever you need something
|
|
|
|
|
custom. No struct tags, no reflection, no dependencies outside the
|
|
|
|
|
standard library.
|
|
|
|
|
|
|
|
|
|
```go
|
|
|
|
|
username := validate.All(
|
|
|
|
|
validate.MinLength(3),
|
|
|
|
|
validate.MaxLength(16),
|
|
|
|
|
validate.Chars("abcdefghijklmnopqrstuvwxyz0123456789_"),
|
|
|
|
|
func(s string) error {
|
|
|
|
|
if strings.HasPrefix(s, "_") {
|
|
|
|
|
return errors.New("must not start with an underscore")
|
|
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
},
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
if err := username(input); err != nil {
|
|
|
|
|
// errors.Is(err, validate.ErrDisallowedChars) still works
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## Why this and not one of the established packages
|
|
|
|
|
|
|
|
|
|
| Package | Style |
|
|
|
|
|
|---|---|
|
|
|
|
|
| [`go-playground/validator`](https://github.com/go-playground/validator) | struct tags (`validate:"required,email"`) driven by reflection |
|
|
|
|
|
| [`go-ozzo/ozzo-validation`](https://github.com/go-ozzo/ozzo-validation) | rules composed in code, through a `Rule` interface over `interface{}` |
|
|
|
|
|
| [`asaskevich/govalidator`](https://github.com/asaskevich/govalidator) | a bag of `IsEmail` / `IsURL` string helpers |
|
|
|
|
|
|
|
|
|
|
This package is the code-composition style (closest to ozzo-validation)
|
|
|
|
|
with two constraints held on purpose:
|
|
|
|
|
|
|
|
|
|
- **Validators are ordinary generic functions.** `In`, `Equal` and the
|
|
|
|
|
rest are type-checked by the compiler — pass the wrong type and it does
|
|
|
|
|
not build. The tag/reflection libraries can't do this; they predate
|
|
|
|
|
generics.
|
|
|
|
|
- **Errors are plain sentinels.** `errors.Is(err, ErrInvalidEmail)`
|
|
|
|
|
composes with the standard `errors` package. There is no bespoke
|
|
|
|
|
`ValidationErrors` type to learn. `Err` matches any error from the
|
|
|
|
|
package.
|
|
|
|
|
|
|
|
|
|
Everything is value-level: there is no struct walker. You wire fields
|
|
|
|
|
together yourself (a few lines) and decide how to present the result.
|
|
|
|
|
|
|
|
|
|
## Fail fast or collect everything
|
|
|
|
|
|
|
|
|
|
```go
|
|
|
|
|
// stops at the first failure
|
|
|
|
|
validate.All(rules...)
|
|
|
|
|
|
|
|
|
|
// runs every rule, joins the failures with errors.Join;
|
|
|
|
|
// errors.Is still matches each one
|
|
|
|
|
validate.Collect(rules...)
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## Building blocks
|
|
|
|
|
|
|
|
|
|
| Group | Functions |
|
|
|
|
|
|---|---|
|
|
|
|
|
| Compose | `All`, `Collect` |
|
|
|
|
|
| Presence / equality | `Required`, `Equal`, `In`, `NotIn` |
|
2026-09-07 16:48:29 +01:00
|
|
|
| String length & content | `MinLength`, `MaxLength` (runes), `MinLengthBytes`, `MaxLengthBytes`, `Chars`, `ExceptChars`, `Prefix`, `Suffix`, `Contains`, `Match` |
|
2026-09-07 14:01:03 +01:00
|
|
|
| Formats | `Email`, `URL`, `UUID` |
|
|
|
|
|
| Numbers | `Min`, `Max`, `MinFloat32`, `MaxFloat32`, `MinFloat64`, `MaxFloat64` |
|
|
|
|
|
| Slices | `MinSize`, `MaxSize` |
|
|
|
|
|
| Errors | `Error`, `NewError`, `Err` |
|
|
|
|
|
|
|
|
|
|
Each returns (or is) a `func(T) error`, so anything you write with the
|
|
|
|
|
same shape composes with them.
|
|
|
|
|
|
|
|
|
|
## Not a fit if…
|
|
|
|
|
|
|
|
|
|
You want **struct-tag validation**, **translated / i18n messages**, or a
|
|
|
|
|
**large catalogue** of built-in checks (credit cards, ISO codes, CIDRs, …).
|
|
|
|
|
Use [`go-playground/validator`](https://github.com/go-playground/validator)
|
|
|
|
|
for that. This package deliberately stays small.
|
2024-06-21 21:54:44 +01:00
|
|
|
|
|
|
|
|
## License
|
|
|
|
|
|
2026-09-07 14:01:03 +01:00
|
|
|
MIT. See [LICENSE.md](./LICENSE.md).
|