这是indexloc提供的服务,不要输入任何密码
Skip to content

Releases: danielgtaylor/huma

v2.34.1

02 Jul 06:12
df27bef
Compare
Choose a tag to compare

Overview

This bugfix release fixes an issue regarding content types that was inadvertently introduced while adding a feature in v2.34.0. The previous behavior is restored while still supporting the new feature.

What's Changed

Full Changelog: v2.34.0...v2.34.1

v2.34.0

30 Jun 16:21
eafeecd
Compare
Choose a tag to compare

Overview

Opt-in for 406 Errors

By default, Huma will fall back to the default format when content negotiation fails to find an appropriate content type that both the client and server can agree on. This enables clients which send no Content-Type header to Just Work ™️, however sometimes that behavior is not desired and you would rather return a 406 Not Acceptable. A new configuration option enables this:

config := huma.DefaultConfig("My API", "1.0.0")
config.NoFormatFallback = true

What's Changed

  • feat: #843 Allow 406 Not Acceptable with NoFormatFallback config by @jh125486 in #850

New Contributors

Full Changelog: v2.33.0...v2.34.0

v2.33.0

23 Jun 04:40
e5fa153
Compare
Choose a tag to compare

Overview

Minimum Go Version Upgrade

Go 1.23+ is now required, keeping to the "last two versions" approach that Go itself uses for support. Dependencies have also been upgraded, including a few dependabots for security issues.

Explicitly Set Empty Example

You can now explicitly set empty example strings:

type MyInput struct {
	MyField string `json:"my_field" example:""`
}

Empty Group Path

It's now possible to use operations on a group with an empty path, having the operation use the group's path without any additions:

grp := huma.NewGroup(api, "/users")

huma.Get(grp, "", func(ctx context.Context, input *struct{}) (*struct{}, error) {
	return nil, nil
})

Adapter Context Unwrapping Fixes

You can now use huma.WithValue and huma.WithContext to wrap a context and the adapter-specific Unwrap function will no longer panic.

subctx := huma.WithValue(ctx, key, "value")
r, w := humago.Unwrap(subctx)

Nested CLI Options

Nested CLI options via structs are now supported.

type DatabaseConfig struct {
    Host     string `doc:"Database host"`
    Port     int    `doc:"Database port" default:"5432"`
    Username string `doc:"Database username"`
}

type AppConfig struct {
    Debug bool            `doc:"Enable debug mode"`
    DB    *DatabaseConfig `doc:"Database configuration"` // Here both ptr or direct would have been acceptable.
}

Results in options like --db.host localhost and --db.port 5432.

Other

Various other fixes and feature improvements. Thanks everyone!

What's Changed

  • fix: Allow explicitely setting the "example" tag to the empty string by @mumu1000 in #773
  • chore(docs): add link to IANA auth schemes by @costela in #774
  • minor: use ErrUnknownContentType in more places by @costela in #799
  • Fix panic when sending an empty file field in a form by @Supet1337 in #785
  • Add WithIndex to PathBuffer by @chrisguitarguy in #788
  • fix: make order of SSE events stable in openapi by @culli in #806
  • Allow empty path in group operations by @Serjlee in #807
  • feat: Go 1.23 minimum, modernize codebase by @danielgtaylor in #820
  • Add option to customize the Gorilla Mux route by @RangelReale in #810
  • Make autopatch merge-patch work with nullable fields by @betaprior in #791
  • Incorrect import path for autopatch by @byronmurg in #838
  • add Japanese README (README_JA.md) by @SoraDaibu in #836
  • feat: add humagin.NewContext by @bbedward in #837
  • fix: linter issues by @danielgtaylor in #842
  • feature: improve default response status flow for head method by @hiddenmarten in #830
  • chore(deps): bump github.com/gofiber/fiber/v2 from 2.52.6 to 2.52.7 by @dependabot in #827
  • Fix adapter.Unwrap() not working after huma.WithContext() by @Serjlee in #829
  • Fix: Support for nested pointer struct fields in CLI options by @tomMoulard in #824
  • fix: export of embed properties when struct type is not exported by @iamnoah in #822
  • Unescape path values in chi adapter by @Serjlee in #821
  • chore(deps): bump github.com/go-chi/chi/v5 from 5.1.0 to 5.2.2 in /examples by @dependabot in #845
  • chore(deps): bump github.com/go-chi/chi/v5 from 5.2.1 to 5.2.2 by @dependabot in #846
  • Potential fix for code scanning alert no. 5: Workflow does not contain permissions by @danielgtaylor in #847
  • Revert "fix: export of embed properties when struct type is not exported" by @danielgtaylor in #848

New Contributors

Full Changelog: v2.32.0...v2.33.0

v2.32.0

18 Mar 17:15
4977a7a
Compare
Choose a tag to compare

Overview

HTTP HEAD Convenience Function

A convenience function was added for HTTP HEAD requests.

huma.Head(api, "/path", handler)

Stop HTML-Escaping JSON

HTTP API usage would rarely need to HTML-escape responses, so this default JSON marshaling behavior has been turned off. If you would like to keep the behavior, you can do so by modifying the huma.Config.Formats map. For example, error messages are now more readable:

  • Before: expected number \u003e= 10
  • After: expected number >= 10

Better Integer Validation

A new validation check has been added to present a better error message to the user when an integer is required but a floating point value like 1.5 is passed in. This now results in an expected integer message instead of a JSON unmarshal error.

Groups + Convenience Function Improvements

Groups and convenience functions like huma.Get now play better together. Groups will regenerate the operation ID and operation summary iff those values were auto-generated and have not been modified. This works for groups of groups as well. The following are equivalent:

huma.Get(api, "/v1/users/", handler)

v1 := huma.NewGroup(api, "/v1")
users := huma.NewGroup(v1, "/users")
huma.Get(users, "/", handler)

fmt.Println(api.OpenAPI().Paths["/v1/users/"].Summary)
// Output: Get v1 users

If you prefer full control over the operation ID and summary, use huma.Register instead. You can still use group operation modifiers and convenience modifiers which modify the operation ID and/or summary and, if modified, they will not get regenerated. You can also disable generation by changing or unsetting the operation's _convenience_id and _convenience_summary metadata fields which are added by convenience functions like huma.Get/huma.Put/etc.

What's Changed

New Contributors

Full Changelog: v2.31.0...v2.32.0

v2.31.0

10 Mar 17:22
6f2a42b
Compare
Choose a tag to compare

Overview

Go 1.24 omitzero Support!

Huma now supports Go's new JSON omitzero feature out of the box, treating it similar to the existing omitempty in terms of making fields optional. The updated rules for optional fields now look like this:

  1. Start with all fields required.
  2. If a field has omitempty, it is optional.
  3. If a field has omitzero, it is optional.
  4. If a field has required:"false", it is optional.
  5. If a field has required:"true", it is required.

See https://huma.rocks/features/request-validation/#optional-required for more info.

What's Changed

New Contributors

Full Changelog: v2.30.0...v2.31.0

v2.30.0

25 Feb 18:09
b02b165
Compare
Choose a tag to compare

Overview

Sponsors

A big thank you to our new sponsor:

Groups

Huma now supports groups, which port over much of the functionality from @cardinalby's excellent https://github.com/cardinalby/hureg library (thank you for that work!). This enables creating groups of operations with the same path prefixes, middleware, operation modifiers, and transformers. Typical usage might look like this:

grp := huma.NewGroup(api, "/v1")
grp.UseMiddleware(authMiddleware)

// Register a `GET /v1/users` route that requires auth.
huma.Get(grp, "/users", func(ctx context.Context, input *struct{}) (*UsersResponse, error) {
	// ...
})

See https://huma.rocks/features/groups/ for more details.

Context Unwrapping

Due to many user requests, it is now possible to "unwrap" a router-specific context into its constituent router-specific representation. Each adapter package now has an Unwrap(huma.Context) T function that will return either a request/response pair or that router's own context type, allowing you to effectively escape Huma in router-agnostic middleware & resolvers.

Caution

You must use the same adapter package to create the API and call Unwrap or Huma will panic!

Example usage:

router := http.NewServeMux()
api := humago.New(router, huma.DefaultConfig("My API", "1.0.0"))

api.UseMiddleware(func(ctx huma.Context, next func(huma.Context)) {
	r, w := humago.Unwrap(ctx)

	// Do something with the request/response.
	// ...

	next(ctx)
})

While generally not recommended, this can help you to use router-specific middleware as you migrate large existing projects to Huma, or just escape Huma's abstractions when they no longer make sense for your use-case. Sometimes the best library is the one that gets out of the way.

See https://huma.rocks/features/middleware/#unwrapping for more details.

What's Changed

New Contributors

Full Changelog: v2.29.0...v2.30.0

v2.29.0

18 Feb 21:11
68c578f
Compare
Choose a tag to compare

Overview

Support More Multipart Form Values

This enabled the use of fields with arbitrary types in the form which will get parsed & validated for you:

huma.Register(api, huma.Operation{
	OperationID: "upload-and-decode-files"
	Method:      http.MethodPost,
	Path:        "/upload",
}, func(ctx context.Context, input *struct {
	RawBody huma.MultipartFormFiles[struct {
		MyFile                    huma.FormFile   `form:"file" contentType:"text/plain" required:"true"`
		SomeOtherFiles            []huma.FormFile `form:"other-files" contentType:"text/plain" required:"true"`
		NoTagBindingFile          huma.FormFile   `contentType:"text/plain"`
		MyGreeting                string          `form:"greeting", minLength:"6"`
		SomeNumbers               []int           `form:"numbers"`
		NonTaggedValuesAreIgnored string  // ignored
	}]
}) (*struct{}, error) {
	// ...
})

Better Auto-patch Support with Sub-routers

The auto-patch functionality now tries to find a common path prefix so it's possible to do stuff like this and have the generated PATCH operation function correctly:

func main() {
    router := chi.NewRouter()
    router.Route("/api", apiMux())
    err = http.ListenAndServe(fmt.Sprintf(":%d", 8080), router)
}

// apiMux returns a function that initializes the API routes
func apiMux() func(chi.Router) {
        return func(router chi.Router) {
            humaConfig := huma.DefaultConfig("API", "dev")
            humaConfig = openapi.WithAuthSchemes(humaConfig)
            humaConfig = openapi.WithOverviewDoc(humaConfig)
            humaConfig = openapi.WithServers(humaConfig, config)
            api := humachi.New(router, humaConfig)
            huma.Register(api, huma.Operation{
                Method:      "GET",
                Path:        "/ressources/{id}",
            }, getRessourceByID)
            huma.Register(api, huma.Operation{
                Method:      "PUT",
                Path:        "/ressources/{id}",
            }, updateRessourceByID)
            autopatch.AutoPatch(api)
    }
}

Custom Param Type Enhancements

Two new interfaces enable some additional advanced customization enhancements when creating operation input parameters:

type ParamWrapper interface {
	Receiver() reflect.Value
}

type ParamReactor interface {
	OnParamSet(isSet bool, parsed any)
}

These can be used like so:

type OptionalParam[T any] struct {
	Value T
	IsSet bool
}

// Define schema to use wrapped type
func (o OptionalParam[T]) Schema(r huma.Registry) *huma.Schema {
	return huma.SchemaFromType(r, reflect.TypeOf(o.Value))
}

// Expose wrapped value to receive parsed value from Huma
// MUST have pointer receiver
func (o *OptionalParam[T]) Receiver() reflect.Value {
	return reflect.ValueOf(o).Elem().Field(0)
}

// React to request param being parsed to update internal state
// MUST have pointer receiver
func (o *OptionalParam[T]) OnParamSet(isSet bool, parsed any) {
	o.IsSet = isSet
}

Fix Panic from External Schema

It's possible to use the default schema transformers now with custom external schemas without causing a panic. For example:

Responses: map[string]*huma.Response{
    "200": {
        Content: map[string]*huma.MediaType{
            "application/json": {
                Schema: &huma.Schema{
                    Ref: "https://json-schema.org/draft/2020-12/schema",
                },
            },
        },
    },
},

Note: external schemas are not validated and are just there for informational purposes and to help with client generation.

Fiber Fixes

A major rework of the humafiber adapter was done in #725. This ensures tests are run with the -race detector and fixes a race that was present in the Fiber adapter. It should be much more stable now.

Deep Object Support for Params

Params now support the OpenAPI deepObject style, enabling e.g. query params to send structured input in that style.

// GreetingOutput represents the greeting operation response.
type GreetingOutput struct {
	Body struct {
		Person Person            `json:"person"`
		Map    map[string]string `json:"map"`
	}
}

type Person struct {
	Name     string `json:"name"`
	Age      int    `json:"age,omitempty" default:"20"`
	Birthday string `json:"birthday,omitempty"`
}

func main() {
	// Create a new router & API
	router := chi.NewMux()
	api := humachi.New(router, huma.DefaultConfig("My API", "1.0.0"))

	// Register GET /greeting
	huma.Get(api, "/greeting", func(ctx context.Context, input *struct {
		Person Person            `query:"person,deepObject"`
		Map    map[string]string `query:"map,deepObject"`
	}) (*GreetingOutput, error) {
		out := &GreetingOutput{}
		out.Body.Person = input.Person
		out.Body.Map = input.Map
		return out, nil
	})

	// Start the server!
	log.Println("http://127.0.0.1:8888/docs")
	http.ListenAndServe("127.0.0.1:8888", router)
}

Example request:

curl --request GET \
  --url 'http://127.0.0.1:8888/greeting?person%5Bname%5D=foo&map%5Ba%5D=foo&map%5Bb%5D=foo2' \
  --header 'Accept: application/json, application/problem+json'

Other fixes

  • Prevent double validation errors
  • Stop Huma from overwriting a custom request body schema when a Body field is present.

What's Changed

New Contributors

Full Changelog: v2.28.0...v2.29.0

v2.28.0

20 Jan 19:43
1c3924e
Compare
Choose a tag to compare

Overview

Upgraded Documentation

Stoplight Elements has been upgraded to the latest version.

Others

  • Fixed two important memory access issues (body race & Fiber context).
  • Additional docs & examples added ❤️
  • Major refactor of some of the codebase to make future changes easier.

What's Changed

New Contributors

Full Changelog: v2.27.0...v2.28.0

v2.27.0

09 Dec 18:14
e73d655
Compare
Choose a tag to compare

Sponsors

A big thank you to our new sponsors:

Consider sponsoring the project!

Overview

Write Errors & Warnings to Stderr

When writing custom commands that output to stdout, sometimes an error or warning can be generated by Huma, which could add unwanted output to stdout. This has been updated to use stderr so it is easier to differentiate. This is useful for an openapi command that dumps the OpenAPI document to stdout as it is now safe to redirect it to a file even if warnings are generated.

Better Handling of Embedded Header Fields

When headers are embedded in the output struct they are now properly referenced in the documentation and the parent embedded struct itself is ignored (previously it would include the headers twice). For example, this now works as expected:

// PaginationOutput contains reusable response headers
// for implementing pagination
type PaginationOutput struct {
	Link   string `header:"Link" doc:"HTTP links for e.g. pagination"`
	Cursor string `header:"Cursor" doc:"Identifier that can be used to paginate. If the value is empty then there are no more pages to iterate over."`
}

// list_slates.go
type listSlateResponse struct {
	pagination.PaginationOutput
	Body []listSlateBody
}

Fiber UserContext Support

When using the Fiber adapter and getting items from the context it now first checks Fiber's UserContext before checking the underlying context for the value. This makes Huma easier to use with Fiber and Fiber-specific middleware. No change in behavior is needed, things should Just Work™️.

Remove Chi From Tests

The code has been refactored to remove reliance on Chi for the tests, simplifying the project overall and relying more on the standard library.

Fix Operation Callbacks

Operation callbacks mistakenly used the wrong type of map[string]*PathItem when it should really have been map[string]map[string]*PathItem instead. The structure should look something like this, which is now supported properly to document asynchronous callbacks that your operation supports:

paths:
  /create:
    post:
      callbacks:
        myEvent:
          "{$request.body#/callbackUrl}":
            post:
              requestBody: # Contents of the callback message
                

Better Support for Embedded RawBody Field

It's now possible to embed the RawBody field and have things work. For example:

type RequestHeader struct {
	Test string `header:"Test"`
}

type EmbedGreeting struct {
	RawBody multipart.Form
}

type AnotherGreetingInput struct {
	RequestHeader
	EmbedGreeting
}

ContentTypeFilter Now Updates OpenAPI

If an operation output implements ContentTypeFilter, then this will be called with the default value application/json and the result used to build the OpenAPI document. For example this will result in application/ld+json in the OpenAPI rather than application/json:

type Response struct {
	Message string `json:"message" doc:"The message."`
}

func (r *Response) ContentType(t string) string {
	return "application/ld+json"
}

type Output struct {
	Body Response
}

Other Fixes

  • Various doc fixes / improvements
  • New linters enabled for better code quality

What's Changed

New Contributors

Full Changelog: v2.26.0...v2.27.0

v2.26.0

13 Nov 17:51
f71eb50
Compare
Choose a tag to compare

Overview

Better Marking of Visited Types

When looking for params, headers, defaults, and resolvers the Huma type traversal code now tracks previously visited types more narrowly, continuing to detect recursive loops while allowing multiple adjacent fields to use the same type. Before this fix it would ignore some fields. For example, this now works propertly to run the resolver on both HomeAddress and AwayAddress:

type Address struct {
	Line1       string `json:"line1" required:"true" minLength:"1" maxLength:"50"`
	Line2       string `json:"line2,omitempty" required:"false" minLength:"0" maxLength:"50" default:""`
	City        string `json:"city" required:"true" minLength:"1" maxLength:"64"`
	State       string `json:"state" required:"true" minLength:"1" maxLength:"32"`
	Zip         string `json:"zip" required:"true" minLength:"1" maxLength:"16"`
	CountryCode string `json:"countryCode" required:"false" minLength:"1" maxLength:"2" default:"US"`
}

func (a Address) Resolve(_ huma.Context, prefix *huma.PathBuffer) []error {
	/* ... do stuff ... */
}

type TestRequestBody struct {
	Name        string  `json:"name"`
	Age         int     `json:"age"`
	HomeAddress Address `json:"home" required:"true"`
	AwayAddress Address `json:"away" required:"true"`
}

More Resilient Fast Q Value Selection

Several minor bugs have been fixed in the fast zero-allocation q value parsing for client-based content negotiation via the Accept header. Values like a;, no longer cause a panic. Several new tests were added to ensure robustness.

No Longer Panic From Client Disconnect

When a client disconnects and a write to the socket results in an error, we now check if the context is context.Canceled and ignore it. This should not result in a panic as that has a negative impact on metrics and perceived service health. An attempt is made to tell the huma.Context that the response status code should be 499 Client Disconnected to help with metrics/logging middleware.

Others

  • Fixed doc bug
  • Minor fix when printing response bodies in tests if JSON indenting fails
  • Refactored code for sending responses to be more consistent & re-used between the handlers and WriteErr and ensure NewErrorWithContext is used everywhere. This should be a no-op for library users.

What's Changed

New Contributors

Full Changelog: v2.25.0...v2.26.0