Idiomatic, generics-based, zero-reflect runtime schema validation and serialization library for Go, faithfully ported from pure Zod v4.
golang-zod brings the developer experience (DX) of TypeScript's Zod v4 to the Go ecosystem: no custom struct tags (z.Struct reuses the standard json tag; z.Object needs none), zero reflection at runtime, first-class i18n out of the box, and an introspectable runtime schema model that exports effortlessly to JSON Schema, OpenAPI 3.1, and GraphQL SDL.
z.Email(), z.UUID(), z.ISO.DateTime(), z.Int32()), .Meta(func(m *z.MetaBuilder) ...) closure builders, and zero v3 deprecation baggage.z.Struct[T] runs reflection exactly once during schema definition; at runtime, fields are assigned directly via high-performance pointer offset mechanics with zero reflection overhead.ParseJSON and Decode read the input token by token with jsontext in a single pass; integers a float64 cannot hold exactly (Int64, Uint64, BigInt) keep full precision.parseCtx uses an inline [8]PathSegment array for shallow paths with automatic spill to slice, yielding 0 allocs/op for path traversals during successful parses.en), Brazilian Portuguese (pt-BR), and Spanish (es), with automatic Accept-Language negotiation.z.Codec(in, out, decode, encode) and presets (z.StringBool(), z.Codecs.*) for reverse encoding and deserialization pipelines.!).z.FromJSONSchema(data) builds dynamic runtime validation schemas from external schema specs.Install golang-zod using go get:
go get github.com/leandroluk/golang-zod
package main import ( "fmt" z "github.com/leandroluk/golang-zod" ) func main() { userSchema := z.Object(func(s *z.ObjectBuilder) { s.Field("id", z.UUID()) s.Field("email", z.Email()) s.Field("name", z.String().Min(2).Trim()) s.Field("age", z.Int().GTE(18).Optional()) s.Field("role", z.Enum("admin", "member", "guest")) }).Meta(func(m *z.MetaBuilder) { m.ID("User"). Title("User Profile"). Description("Registered system user entity") }) input := map[string]any{ "id": "123e4567-e89b-12d3-a456-426614174000", "email": "alice@example.com", "name": " Alice ", "age": 28, "role": "admin", } user, err := userSchema.Parse(input) if err != nil { panic(err) } fmt.Printf("Validated: %v\n", user["name"]) // "Alice" (trimmed) }
type User struct { ID string `json:"id"` Email string `json:"email"` Name string `json:"name"` Age int `json:"age"` } var userStructSchema = z.Struct[User](func(s *z.StructBuilder, u *User) { s.Field(&u.ID, z.UUID()) s.Field(&u.Email, z.Email()) s.Field(&u.Name, z.String().Min(2).Trim()) s.Field(&u.Age, z.Int().GTE(18)) }) func parseUser(rawJSON []byte) (User, error) { return userStructSchema.ParseJSON(rawJSON) }
import "context" func handleRequest(ctx context.Context, payload any, acceptLang string) { // Derive request context with negotiated language reqCtx := z.WithLocale(ctx, acceptLang) user, err := userSchema.Parse(payload, reqCtx) if err != nil { if zerr, ok := err.(*z.ZodError); ok { // Issues are translated automatically into pt-BR, es, or en for _, iss := range zerr.Issues() { fmt.Printf("[%s] %s: %s\n", iss.PathString(), iss.Code, iss.Message) } } } }
golang-zod introduces a hybrid architecture for Go structs:
z.Struct[T](...), field pointers (&u.Field) are correlated to struct field offsets using reflect.unsafe.Add confined strictly to struct_unsafe.go).reflect is never imported or called during parsing, yielding sub-microsecond validation and minimal heap allocations.| TypeScript Zod v4 | golang-zod | Notes |
|---|---|---|
z.string().min(3) |
z.String().Min(3) |
Chained validation methods |
z.email(), z.uuid() |
z.Email(), z.UUID() |
First-class top-level formats |
z.object({ name: z.string() }) |
z.Object(func(s *z.ObjectBuilder) { s.Field("name", z.String()) }) |
Dynamic map[string]any output |
z.infer<typeof schema> |
z.Struct[T](...) |
Direct typed struct compilation |
.describe("...") |
.Meta(func(m *z.MetaBuilder) { m.Description("...") }) |
Fluent immutable metadata builder |
schema.meta() |
z.GetMeta(schema) |
Top-level metadata accessor |
z.enum(["a", "b"]) |
z.Enum("a", "b") |
Generic variadic string enum |
z.union([a, b]) |
z.Union(a, b) |
First-matching candidate |
z.discriminatedUnion("type", [...]) |
z.DiscriminatedUnion("type", a, b) |
O(1) discriminator map dispatch |
z.lazy(() => schema) |
z.Lazy(func() z.Schema[T] { return ... }) |
Memoized recursive data structures |
z.codec(in, out, dec, enc) |
z.Codec(in, out, dec, enc) |
Bidirectional serialization pipelines |