Huma

repository·main·Indexed 26 days ago

https://github.com/danielgtaylor/huma

A modern, fast, and flexible micro framework for building HTTP REST/RPC APIs in Go. Backed by OpenAPI 3.1 and JSON Schema, Huma provides automatic documentation, request validation, and high-quality developer tooling. It supports multiple router adapters (such as Chi, Gin, Fiber, and the Go standard library) and includes built-in documentation renderers like Scalar, Stoplight Elements, and SwaggerUI.

Tokens
47.7K
Snippets
125
Records
235
Agent score
86%

What's inside Huma

  1. Introduction to Huma

    main
    Huma is a modern, simple, fast, and flexible micro framework for building HTTP REST/RPC APIs in Golang. It is backed by OpenAPI 3 and JSON Schema, ensuring that your API documentation and validation are always in sync with your implementation.
  2. Overview of Huma features

    main

    Huma is a micro framework for building HTTP REST/RPC APIs in Go, designed to be backed by OpenAPI 3.1 and JSON Schema. It is built to allow incremental adoption by letting you bring your own router, middleware, and logging/metrics.

    Key capabilities include:

    • Declarative API Definition: Define operations, models, request parameters (path, query, header), request bodies, responses (including errors), and response headers.
    • Automatic Documentation: Generates OpenAPI specifications and JSON Schemas from annotated Go types, ensuring documentation stays in sync with code.
    • Validation & Typing: Provides static typing for parameters and automatic input model validation with error handling.
    • Content Negotiation: Supports JSON (RFC 8259) and optional CBOR (RFC 7049) via the Accept header.
    • Standardized Errors: Uses RFC 9457 JSON Errors (application/problem+json) by default.
    • Advanced HTTP Support: Includes conditional request utilities (e.g., If-Match) and per-operation request size limits.
  3. Overview of Huma features and capabilities

    main

    Huma is a production-ready Go micro-framework designed to accelerate API development with fewer bugs. Key features include:

    • Standards-Based: Built on OpenAPI and JSON Schema for automatic documentation and tool compatibility.
    • Extensibility: Supports both router-specific and router-agnostic middleware, custom request validation via resolvers, and customizable OpenAPI/JSON Schema generation.
    • Guardrails: Provides strongly-typed models with compile-time checks, automatic input validation, and automatic response serialization based on content-negotiation.
    • Automation: Enables automatic generation of CLI tools and SDKs from your API definitions.
  4. Core Goals of Huma

    main

    Huma is designed with the following objectives:

    • Modern API Backend: Provides a framework for Go developers focused on OpenAPI 3.1 and JSON Schema.
    • Incremental Adoption: Allows teams to bring their own router, middleware, and logging/metrics. It includes an extensible OpenAPI & JSON Schema layer to document existing routes without a full rewrite.
    • Guard Rails: Designed to prevent common API development mistakes.
    • Self-Documenting: Ensures documentation stays up to date with the code.
    • Developer Tooling: Provides high-quality generated tooling for developers.
  5. Understand the Huma request flow

    main

    A request follows these steps before reaching your handler and being returned as a response:

    1. Unmarshal: Reads raw request body bytes into a Go structure.
    2. Validate: Checks input constraints (e.g., minimum, maxLength).
    3. Resolve: Runs custom validation code.
    4. Operation: Your handler function executes business logic.
    5. Transform: Modifies structured response data before marshaling.
    6. Marshal: Converts structured response data into bytes (e.g., JSON).
  6. Huma Overview and Key Features

    main

    Huma is a modern, high-performance micro-framework for building HTTP REST/RPC APIs in Go, powered by OpenAPI 3 and JSON Schema.

    Key features include:

    • Declarative Interface: Define operations, models, and request parameters (path, query, header, cookie, body) using Go types and annotations.
    • Automatic Documentation: Generates OpenAPI 3.1 specifications and interactive documentation (e.g., via Stoplight Elements).
    • Automatic Validation: Validates input models and handles errors automatically.
    • Content Negotiation: Supports JSON (RFC 8259) and optionally CBOR (RFC 7049) via the Accept header.
    • Standard Compliance: JSON errors follow RFC 9457 (application/problem+json).
    • Extensibility: Works with any router (e.g., chi, std/net/http) and supports middleware/logging integration.
  7. Implement Huma Auth Middleware for JWT Validation

    main

    If you are not using an API Gateway to handle authentication, you can implement custom middleware within Huma to validate incoming JWTs.

    Typical implementation steps:

    1. Fetch JWKS: Use a library like github.com/lestrrat-go/jwx/v2/jwk to create a cached, auto-refreshing key set from your issuer's JWKS URL.
    2. Check Operation Security: In the middleware, inspect ctx.Operation().Security to determine if the current endpoint requires authorization.
    3. Validate Token: Extract the Bearer token from the Authorization header and validate it using the JWKS, issuer, and audience.
    4. Verify Scopes: Extract the scopes claim from the parsed JWT and ensure it contains the scopes required by the operation.
    5. Register Middleware: Attach the middleware to your API using api.UseMiddleware().
  8. Group operations with huma.NewGroup

    main

    You can group operations under common route prefixes and share middleware, operation modifiers, and response transformers using huma.NewGroup. A group wraps a huma.API instance and can be passed to huma.Register or convenience wrappers like huma.Get and huma.Post.

    Note: Groups assume that huma.Register or its convenience wrappers are used. If you are registering operations manually, you must call group.DocumentOperation(*huma.Operation) to ensure they are included in the documentation.

    grp := huma.NewGroup(api, "/v1")
    grp.UseMiddleware(authMiddleware)
    
    huma.Get(grp, "/users", func(ctx context.Context, input *struct{}) (*UsersResponse, error) {
    	// ...
    })
  9. Control field requirement (Optional vs Required)

    main

    Huma determines if a field is required or optional based on the following priority logic:

    1. Default Behavior:
      • Path parameters are always required.
      • Cookie, header, and query parameters are optional by default.
      • All other fields (request bodies, multipart forms) are required by default.
    2. Overriding to Optional: A field becomes optional if it has:
      • omitempty in the json tag.
      • omitzero in the json tag.
      • required:"false" tag.
    3. Overriding to Required: A field is required if it has required:"true".

    Global Configuration: To make all fields optional by default (overriding the default requirement for bodies), set FieldsOptionalByDefault in your huma.Config.

    config := huma.DefaultConfig("My API", "1.0.0")
    config.FieldsOptionalByDefault = true
  10. Install Restish for API testing

    main

    Huma does not include a built-in CLI client, but you can use Restish to interact with your API. Restish provides a high-level interface that converts OpenAPI operations into command-line arguments and generates help documentation.

    Installation by Platform

    macOS

    # Homebrew
    brew install restish
    
    # Go
    go install github.com/rest-sh/restish/v2/cmd/restish@latest

    Linux

    # Go
    go install github.com/rest-sh/restish/v2/cmd/restish@latest
    
    # Homebrew for Linux
    brew install restish

    Windows

    # Go
    go install github.com/rest-sh/restish/v2/cmd/restish@latest
    # Homebrew
    $ brew install restish
    
    # Go
    $ go install github.com/rest-sh/restish/v2/cmd/restish@latest
  11. Set a dynamic HTTP status code in a response

    main

    If the status code depends on the logic within your handler, include a field named Status of type int in your response struct. Note that using DefaultStatus in huma.Operation is generally preferred unless the status must change per request.

    type ThingResponse struct {
    	Status int
    }
    
    huma.Register(api, huma.Operation{
    	OperationID: "get-thing",
    	Method:      http.MethodGet,
    	Path:        "/things/{thing-id}",
    	Summary:     "Get a thing by ID",
    }, func(ctx context.Context, input *ThingRequest) (*ThingResponse, error) {
    	// Create a response and set the dynamic status
    	resp := &ThingResponse{}
    	if input.ID < 500 {
    		resp.Status = 200
    	} else {
    		// This is a made-up status code used for newer things.
    		resp.Status = 250
    	}
    	return resp, nil
    })