Awesome
<div align="center"> <br /> <a href="https://github.com/Oudwins/zog"> <img src="https://raw.githubusercontent.com/Oudwins/zog/master/assets/zog-banner.png" alt="Zog, a Zod-like schema parser & validator" /> </a> </div>ZOG - A Zod & Yup like Schema Parser & Validator for GO
<a href="https://pkg.go.dev/github.com/Oudwins/zog"><img src="https://pkg.go.dev/badge/github.com//github.com/Oudwins/tailwind-merge-go.svg" alt="Go Reference" /></a>
Zog is a schema builder for runtime value parsing and validation. Define a schema, transform a value to match, assert the shape of an existing value, or both. Zog schemas are extremely expressive and allow modeling complex, interdependent validations, or value transformations. Checkout the full docs at https://zog.dev
Killer Features:
- Concise yet expressive schema interface, equipped to model simple to complex data models
- Zod-like API, use method chaining to build schemas in a typesafe manner
- Extensible: add your own validators, schemas and data providers
- Rich error details, make debugging a breeze
- Almost no reflection when using primitive types
- Built-in coercion support for most types
- Zero dependencies!
- Three Helper Packages
- zenv: parse environment variables
- zhttp: parse http forms & query params
- i18n: Opinionated solution to good i18n zog errors
API Stability:
- I will consider the API stable when we reach v1.0.0
- However, I believe very little API changes will happen from the current implementation. The APIs are are most likely to change are the data providers (please don't make your own if possible use the helpers whose APIs will not change meaningfully) and the ParseCtx most other APIs should remain the same
- Zog will not respect semver until v1.0.0 is released. Expect breaking changes (mainly in non basic apis) until then.
Introduction
0. Read the docs at zog.dev
Or don't, below is the quickstart guide
1 Install
go get github.com/Oudwins/zog
2 Create a user schema and its struct
import (
z "github.com/Oudwins/zog"
)
type User struct {
Name string `zog:"firstname"` // tag is optional. If not set zog will check for "name" field in the input data
Age int
}
var userSchema = z.Struct(z.Schema{
// its very important that schema keys like "name" match the struct field name NOT the input data
"name": z.String().Min(3, z.Message("Override default message")).Max(10),
"age": z.Int().GT(18).Required(z.Message("is required")),
})
3 Parse into the struct
func main() {
u := User{}
m := map[string]string{
"firstname": "Zog", // Note we are using "firstname" here as specified in the struct tag
"age": "", // won't return an error because fields are optional by default
}
errsMap := schema.Parse(m, &u)
if errsMap != nil {
// handle errors -> see Errors section
}
u.Name // "Zog"
// note that this might look weird but we didn't say age was required so Zog just skiped the empty string and we are left with the uninitialized int
u.Age // 0
}
4. Its easy to use with http
The zhttp package has you covered for JSON, Forms and Query Params, just do:
import (
zhttp "github.com/Oudwins/zog/zhttp"
)
err := userSchema.Parse(zhttp.Request(r), &user)
5. Or to validate your environment variables
The zenv package has you covered, just do:
import (
zenv "github.com/Oudwins/zog/zenv"
)
err := envSchema.Parse(zenv.NewDataProvider(), &envs)
6. You can also parse individual fields
var t = time.Time
errsList := Time().Required().Parse("2020-01-01T00:00:00Z", &t)
7 And do stuff before and after parsing
var dest []string
Slice(String().Email().Required()).PreTransform(func(data any, ctx z.ParseCtx) (any, error) {
s := val.(string)
return strings.Split(s, ","), nil
}).PostTransform(func(destPtr any, ctx z.ParseCtx) error {
s := val.(*[]string)
for i, v := range s {
s[i] = strings.TrimSpace(v)
}
return nil
}).Parse("foo@bar.com,bar@foo.com", &dest) // dest = [foo@bar.com bar@foo.com]
Roadmap
These are some of the things I want to add to zog before v1.0.0
- Support for schema.Clone()
- support for catch & default for structs & slices
- Struct generation from the schemas
- Validate method that will not parse but rather just validate a struct against the schema
Support
The damm domain costs me some outrageous amount like 100$ a year, so if any one wants to help cover that cost through github sponsors that is more than welcome.
Acknowledgments
- Big thank you to @AlexanderArvidsson for being there to talk about architecture and design decisions. It helped a lot to have someone to bounce ideas off of
- Credit for all the inspiration goes to /colinhacks/zod & /jquense/yup
- Credit for the initial idea goes to anthony (@anthonyGG) -> /anthdm/superkit he made a hacky version of this idea that I used as a starting point, I was never happy with it so I inspired me to rewrite it from scratch. I owe him a lot
- Credit for the zod logo goes to /colinhacks/zod
License
This project is licensed under the MIT License - see the LICENSE file for details.