zog

repository·master·Indexed 22 days ago

https://github.com/oudwins/zog

A high-performance, zero-dependency schema builder and validator for Go, inspired by Zod and Yup. It provides a typesafe, method-chaining API for parsing, transforming, and validating complex data structures at runtime. Zog includes helper packages such as zhttp for HTTP requests, zjson for JSON data, and zenv for environment variables.

Tokens
45.2K
Snippets
173
Records
212
Agent score
78%

What's inside zog

  1. Introduction to Zog schema parsing and validation

    master

    Zog is a schema builder for Go designed for runtime value parsing and validation. It allows you to define schemas to perform one or both of the following tasks:

    1. Transform a value: Convert an input value into a shape that matches your defined schema.
    2. Assert a shape: Validate that an existing value conforms to a specific structure.

    Zog features a Zod-like API using method chaining for typesafe schema construction, supports built-in type coercion, and provides rich error context for debugging. It is designed to be extensible via custom Tests and Schemas.

  2. What is Zog Context and how to use it

    master

    Zog uses the z.Ctx interface to pass information related to a specific schema.Parse() or schema.Validate() call. It allows you to manage issues, retrieve custom data passed during execution, and influence how errors are formatted.

    Key capabilities include:

    • Manual Issue Creation: Use AddIssue to report validation errors manually, which is essential for complex custom tests.
    • Custom Data Retrieval: Use Get(key string) to retrieve values passed into the schema via z.WithCtxValue.
    • Issue Generation: Use Issue() to create a new *ZogIssue prefilled with the current schema context's data, allowing you to chain methods like .SetMessage() before adding it to the context.
    type Ctx interface {
    	// Get a value from the context
    	Get(key string) any
    	// Adds an issue to the schema execution.
    	AddIssue(e *ZogIssue)
    
    	// Returns a new issue with the current schema context's data prefilled
    	Issue() *ZogIssue
    }
  3. What is included in ZSS exhaustive metadata

    master

    When the zogmeta build tag is used, the ZSS output is enriched with the following fields:

    • GoTypes: An array of ZSSGoType objects containing:
      • PkgPath: The package path (empty for built-in types).
      • Name: The type name (may be empty for unnamed types).
      • Display: The full type string representation.
    • Format: For TimeSchema, the format string specified via z.Time(z.Time.Format(...)) is included.
    • Custom Messages: Custom messages set via z.Message() are included in the ZSSTest.Message field.

    For generic schemas like PreprocessSchema[F, T] and BoxedSchema[B, T], multiple type parameters are captured in the GoTypes array.

  4. Map environment variables using struct tags

    master

    When parsing environment variables into a Go struct, you can use struct tags to specify which environment variable name corresponds to which field. zenv supports both the env and zog tags.

    Example:

    • Host string env:"DB_HOST"`` maps the environment variable DB_HOST to the Host field.
    • User string zog:"DB_USER"`` maps the environment variable DB_USER to the User field.
    type Config struct {
    	Host string `env:"DB_HOST"` 
    	User string `zog:"DB_USER"` 
    }
  5. Explore Zog helper packages

    master

    Zog provides four specialized helper packages to simplify common parsing tasks:

    • zenv: For parsing environment variables.
    • zhttp: For parsing HTTP forms and query parameters.
    • zjson: For parsing JSON data.
    • i18n: An opinionated solution for internationalizing Zog error messages.
  6. How schema keys relate to Go struct fields and tags

    master

    A common mistake is using input keys (like JSON keys) as schema keys.

    The Mental Model:

    1. Input Data (JSON/Form): Uses json or zog struct tags to map keys to Go fields.
    2. Zog Schema: Operates purely on the Go struct shape. Schema keys must match the Go struct field names (e.g., FirstName), not the input keys (e.g., first_name).

    Zog is unaware of the source data format; it only validates the structure of the Go struct after the tags have performed the mapping.

    type Name struct {
        FirstName string `json:"first_name" zog:"first_name"` // Tags map input 'first_name' to 'FirstName'
        LastName  string `json:"last_name" zog:"last_name"` 
    }
    
    // ✅ Correct: Schema keys match the Go field names
    var schema = z.Struct(z.Shape{
        "FirstName": z.String().Required(z.Message("First name is required")),
        "LastName":  z.String().Required(z.Message("Last name is required")),
    })
  7. Use Transforms to modify data in a pipeline

    master

    Transforms are functions applied to data in a pipeline. They take a pointer to the data as input, allowing them to modify the value in place.

    For primitive types, you can use the specific pointer type (e.g., *string). For complex types (like structs), the input is any, and you must perform a type assertion.

    Transform Function Signature:

    type Transform[T any] func(dataPtr T, ctx Ctx) error
    type User struct {
    	Phone    string
    	AreaCode string
    }
    
    z.Struct(z.Shape{
    	"phone": z.String().Test(...).Transform(func (valPtr *string, ctx z.Ctx) error{
    		*valPtr = strings.ReplaceAll(*valPtr, " ", "") // remove all spaces
    		return nil
    	}),
    }).Transform(func(dataPtr any, ctx z.Ctx) error {
    	user := dataPtr.(*User)
    	user.AreaCode = user.Phone[:3]
    	user.Phone = user.Phone[3:]
    	return nil
    })
  8. Understanding Zog Panics

    master

    Zog follows TigerStyle asserts. It is designed to panic when its fundamental assumptions are broken, which typically indicates a configuration error rather than an input data error.

    Key distinction:

    • Input data is wrong: Zog will not panic; it will return validation errors.
    • Configuration is wrong: Zog will panic. This usually means your schema definition is invalid or incompatible with the destination types you are providing.
  9. Important considerations for recursive schemas

    master

    When working with recursive schemas in ZOG, keep the following behaviors in mind:

    • Lazy Initialization: The schema is only materialized when first accessed, meaning the initial call will be slightly slower.
    • Thread-Safety: Recursive schemas are safe for concurrent use across multiple goroutines.
    • Nil Handling: Recursive references can be nil. Use z.Ptr() for fields that may be optional or null.
    • Type Safety: Ensure your Go struct definition matches the structure defined in your schema.
  10. Understand the ZogIssue structure

    master

    In Zog, validation failures do not throw exceptions. Instead, they return a ZogIssueList containing ZogIssue structs. A ZogIssue represents a structured validation result that includes the error code, the location in the data, the invalid value, and a human-readable message. While ZogIssue contains a Message field safe for end-users, it also includes an Err field which wraps the underlying low-level error (like a JSON unmarshalling failure) for internal debugging.

    // ZogIssue represents an issue that occurred during parsing or validation.
    type ZogIssue struct {
    	Code    zconst.ZogIssueCode // Unique identifier for the issue
    	Path    []string           // Path to the field that caused the issue
    	Value   any                // The data value that caused the issue
    	Dtype   string             // The destination type (zconst.ZogType)
    	Params  map[string]any     // Params from the Test that caused the issue
    	Message string             // Human-readable, user-friendly message
    	Err     error              // The wrapped underlying error (if any)
    }
  11. Understand the purpose of the `internals` package

    master

    The zog library provides an internals package which contains code that is not intended for general user space. Unlike the standard Go internal directory (which prevents external imports), zog's internals package is accessible but is considered unstable.

    Key characteristics of internals:

    • Experimental APIs: It is used to host features and APIs that are currently being tested or refined.
    • No Stability Guarantee: Code within internals may undergo breaking changes at any time without notice.
    • Promotion Path: Features often reside in internals for an extended period before being promoted to the main zog package.

    When to use it: You should only use internals if you specifically need to build on top of experimental features that are not yet available in the public API. Use it at your own risk regarding future compatibility.

  12. Configure key mapping using struct tags

    master

    By default, Zog uses the schema field name as the key to look up values in the input data. To map different input keys (like kebab-case or snake_case) to your Go struct fields, you can use several struct tags.

    Supported Tags (in order of priority):

    1. json (for JSON input)
    2. form (for form input)
    3. query (for query string input)
    4. env (for environment variables)
    5. zog (a catch-all tag for any input data)
    6. Schema field name (fallback)

    If multiple tags are present, Zog follows the priority order listed above.

    type User struct {
    	Name     string `zog:"first-name"` // Uses "first-name" from input
    	LastName string `query:"last_name" json:"last-name"` // Priority: query > json
    }
    
    // Example usage with mixed input keys
    z.Struct(z.Shape{"name": z.String(), "lastName": z.String()}).
        Parse(map[string]any{"first-name": "test", "lastName": "Doe"}, &User{})