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.
0. Read the docs at zog.dev
Or don't, below is the quickstart guide
go get github.com/Oudwins/zog
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")),
})
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
}
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)
The zenv package has you covered, just do:
import (
zenv "github.com/Oudwins/zog/zenv"
)
err := envSchema.Parse(zenv.NewDataProvider(), &envs)
var t = time.Time
errsList := Time().Required().Parse("2020-01-01T00:00:00Z", &t)
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("[email protected],[email protected]", &dest) // dest = [[email protected] [email protected]]
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
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.
- 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
This project is licensed under the MIT License - see the LICENSE file for details.