go-z

module
v0.0.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 25, 2026 License: MIT

README

go-z

Test Go Reference Go Report Card

Schema-first validation for Go. Define a schema once, parse anything into it, and get structured errors back — with the ergonomics of Zod and the performance of hand-written Go.

Inspired by Zod, not affiliated with it. go-z is an independent project, not endorsed by or sponsored by Zod or its authors. Portions are derived from Zod under the MIT licence — see NOTICE.

Documentation · Quickstart · API reference · Benchmarks

Install

go get github.com/iKunalChhabra/go-z/z

Requires Go 1.26+. Zero dependencies for the core package; the optional zgin subpackage pulls in Gin.

Quick start

package main

import (
	"fmt"

	"github.com/iKunalChhabra/go-z/z"
)

func main() {
	user := z.Object(z.Shape{
		"name":  z.String().Min(2).Max(100),
		"email": z.String().Email(),
		"age":   z.Int().Gte(0).Lt(150).Optional(),
	})

	data, err := user.Parse(map[string]any{
		"name":  "Ada",
		"email": "ada@example.com",
	})
	if err != nil {
		zerr, _ := z.AsZodError(err)
		fmt.Println(z.Prettify(zerr))
		return
	}
	fmt.Println(data) // map[email:ada@example.com name:Ada]
}

The package is named z and lives at /z, so a plain import already reads the way Zod does — no alias required:

import "github.com/iKunalChhabra/go-z/z"

z.String().Min(5).Email()

Features

  • Zod-shaped API. z.String().Min(5).Email(), z.Object(z.Shape{…}), Optional / Nullable / Default / Catch / Pipe / Transform / Refine.
  • Typed edges. String().Optional().Parse(v) returns (*string, error); String().Default("x").Parse(v) returns (string, error). Generic wrappers keep the inner type instead of collapsing to any.
  • Structured errors. Eleven issue codes with paths, byte-compatible with Zod's JSON, plus Flatten / Format / Treeify / Prettify.
  • Bidirectional codecs. z.Decode / z.Encode with direction-aware defaults.
  • JSON Schema export. z.ToJSONSchema for OpenAPI and client-side validators.
  • i18n. Error maps and seven locales (en es fr de ja pt zh).
  • Concurrency-safe. Schemas are immutable after construction; Parse is lock-free and -race clean. ParseParallelSlice fans large slices across cores.
  • Gin integration. zgin.Validate, zgin.BindJSON, typed zgin.GetAs[T].

Usage

Safe parsing
res := z.String().Email().SafeParse("nope")
if !res.Success {
	fmt.Println(res.Error.Issues[0].Code) // invalid_format
}
Objects, unions, recursion
var Category z.AnySchemaLike
Category = z.Lazy(func() z.AnySchemaLike {
	return z.Object(z.Shape{
		"name":     z.String().Min(1),
		"children": z.Array(Category).Default([]any{}),
	})
})

userOrGuest := z.DiscriminatedUnion("role", []z.AnySchemaLike{
	z.Object(z.Shape{"role": z.Literal("admin"), "perms": z.Array(z.String())}),
	z.Object(z.Shape{"role": z.Literal("guest"), "session": z.String().UUID()}),
})
Structs
type User struct {
	Name  string `json:"name"`
	Email string `json:"email"`
}

parsed, err := z.ToStruct[User](user).Parse(input) // parsed is a User
Codecs
isoDate := z.Codec(z.String().ISODateTime(), z.Time(), z.CodecTx{
	Decode: func(v any, _ *z.RefinementCtx) (any, error) {
		return time.Parse(time.RFC3339Nano, v.(string))
	},
	Encode: func(v any, _ *z.RefinementCtx) (any, error) {
		return v.(time.Time).UTC().Format(time.RFC3339Nano), nil
	},
})

t, _ := z.Decode(isoDate, "2024-01-15T10:30:00Z") // time.Time
s, _ := z.Encode(isoDate, t)                      // ISO string
Gin
import "github.com/iKunalChhabra/go-z/zgin"

r.POST("/users", zgin.Validate(user), func(c *gin.Context) {
	body, _ := zgin.Get(c) // already parsed and validated
	c.JSON(200, body)
})

Failed validation writes Zod-shaped issues automatically:

{"success":false,"error":{"issues":[{"code":"too_small","path":["name"],"message":"Too small: expected string to have >=2 characters"}]}}

Flatten, Treeify, and Prettify renderers are available via zgin.Options.

Concurrency
schema := z.String().Email() // build once, share freely
go func() { schema.Parse(a) }()
go func() { schema.Parse(b) }()

out, err := z.ParseParallelSlice(ctx, itemSchema, items, z.ParallelOpts{})

Performance

4-core Xeon, Go 1.26, median of six runs. Full methodology and tables in BENCHMARKS.md.

Scenario go-z go-playground/validator Oudwins/zog
Flat object 416 ns 607 ns 1295 ns
Nested object 978 ns 1090 ns 2783 ns
String formats (email + uuid + url) 890 ns 1099 ns 1810 ns
Array of 10k (parallel) 2.75 ms 6.09 ms 12.9 ms

Email, UUID, and the ISO date/time formats use hand-written matchers rather than backtracking regexes; each is differential-tested against the regex it replaced over hundreds of thousands of random inputs. Building Zod-shaped issues makes the failure path slower than tag-based validators — see BENCHMARKS.md.

Design notes

  • Untyped core, typed edge. The engine runs on any, like Zod; Schema[T] is the generic boundary. Optional and Nullable yield *T (nil means absent or null); every other wrapper yields T. Each wrapper has a type-erased constructor for heterogeneous containers (Optional(anySchema)) and a typed one (OptionalOf, DefaultOf, RefineOf, …) that works with every schema type.
  • JSON model first. Objects produce map[string]any, arrays []any, and Int()/Number() produce float64. Use Int64() for a typed int64 edge or ToStruct[T] for structs.
  • Missing is not nil. Missing is Zod's undefined (an absent key); nil is JSON null. Optional accepts Missing, Nullable accepts nil, Nullish both.
  • Params are checked at definition time. An unsupported params type panics while the schema is built — at startup, never during request handling.
  • Object field order. Object(Shape) reports issues in sorted key order because Go maps are unordered; ObjectOrdered([]Field{…}) preserves definition order.

Project layout

Every package is a directory; none of them is special.

z/               core package — import "github.com/iKunalChhabra/go-z/z"
  schema_*.go      schema types (string, number, object, union, codec, …)
  checks_*.go      composable checks
  fluent*.go       mid-chain Optional/Default/Refine on concrete schemas
  matchers.go      hand-written format matchers
  errorutils.go    Flatten / Format / Treeify / Prettify
  jsonschema.go    ToJSONSchema
  locale_*.go      i18n error maps
  parallel.go      ParseParallelSlice
  tostruct.go      cached reflect decode
zgin/            Gin binding and middleware
bench/           comparative benchmarks (separate module)
docs/            documentation site

Status

Ports Zod v4 (colinhacks/zod @ 912f0f5): primitives and string formats, objects and collections, unions / xor / discriminated unions / intersection / lazy, wrappers, codecs, ToJSONSchema, template literals, coercion, error utilities, seven locales, Gin, struct binding, and parallel parsing. Zod's own test cases are ported in parity_*_test.go — see PARITY.md.

Not implemented: fromJSONSchema, and the JavaScript-only surface (z.function, z.promise, z.symbol, z.file). Async parsing is unnecessary in Go.

Contributing

Issues and pull requests are welcome. Before submitting:

go test -race ./...
go vet ./...
gofmt -l .

Author

Kunal Chhabra (@iKunalChhabra)

Licence and attribution

MIT — Copyright (c) 2026 Kunal Chhabra. See LICENSE.

Portions of this project are derived from Zod (MIT, Copyright (c) 2025 Colin McDonnell): string-format patterns, locale message text, the issue taxonomy, and behavioural test cases ported from Zod's test suite. Zod's licence is reproduced in full in NOTICE.

Directories

Path Synopsis
z
Package z provides schema-first validation for Go, ported from Zod v4 (https://zod.dev).
Package z provides schema-first validation for Go, ported from Zod v4 (https://zod.dev).

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL