Releases: danielgtaylor/huma
v2.35.0
Overview
Moved this release as it was incorrectly tagged as v2.34.3.
Improved Error Messaging for Form Pointers
Pointer panic messages now explicitly include “Form” where applicable, making debugging clearer and resolving #892.
Expanded String Format Support
Added support for duration and idn-hostname string formats, improving schema expressiveness and validation coverage.
Header Parsing Fix
Fixed several subtle issues in header detection that occurred when output fields were slices, arrays, or maps, which could cause headers (notably []*http.Cookie) to be incorrectly applied or documented. The parsing logic now correctly inspects the root type of collection fields and adds support for hidden headers, allowing them to be excluded from generated OpenAPI documentation. These changes resolve multiple long-standing documentation and serialization bugs without introducing new behavior.
What's Changed
- fix: include “Form” in pointer panic message (fixes #892) by @wolveix in #938
- feat: support duration and IDN-hostname string formats by @wolveix in #939
- docs: add simple HTML docs by @wolveix in #941
- fix: header parsing by @wolveix in #944
- perf(schema): optimize convertType with fast paths for compatible types by @baagod in #931
- Added exhaustive error code wrappers by @rhodeon in #876
New Contributors
Full Changelog: https://github.com/danielgtaylor/huma/compare/v2.34.2...v2.34.3
v2.34.2
Overview
TLS-Aware URL Scheme Detection
Huma now correctly uses the https URL scheme when TLS is configured, and http when it is not. Previously, the scheme detection could be incorrect in certain scenarios.
Time Wrapper Parsing Fix
Fixed an issue where time wrapper types failed to parse correctly, resolving #844.
What's Changed
- fix: correct example format for tags in Pet struct by @vikyw89 in #858
- docs: add links to hire me by @danielgtaylor in #869
- Revert "docs: add links to hire me" by @danielgtaylor in #890
- chore(deps): bump github.com/gofiber/fiber/v2 from 2.52.7 to 2.52.9 by @dependabot[bot] in #882
- Fix code samples in the response output documentation by @prnvbn in #927
- fix: use "http" URL scheme only if no TLS is used by @leonklingele in #905
- fix: failed to parse time wrapper, close #844 by @zhenzou in #924
- Minor linting/formatting by @wolveix in #936
New Contributors
- @vikyw89 made their first contribution in #858
- @prnvbn made their first contribution in #927
- @leonklingele made their first contribution in #905
- @zhenzou made their first contribution in #924
Full Changelog: v2.34.1...v2.34.2
v2.34.1
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
- fix: content type bug introduced in #850 by @danielgtaylor in #854
Full Changelog: v2.34.0...v2.34.1
v2.34.0
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 = trueWhat's Changed
New Contributors
Full Changelog: v2.33.0...v2.34.0
v2.33.0
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
- @mumu1000 made their first contribution in #773
- @Supet1337 made their first contribution in #785
- @chrisguitarguy made their first contribution in #788
- @culli made their first contribution in #806
- @Serjlee made their first contribution in #807
- @RangelReale made their first contribution in #810
- @betaprior made their first contribution in #791
- @byronmurg made their first contribution in #838
- @SoraDaibu made their first contribution in #836
- @bbedward made their first contribution in #837
- @tomMoulard made their first contribution in #824
- @iamnoah made their first contribution in #822
Full Changelog: v2.32.0...v2.33.0
v2.32.0
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 usersIf 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
- feat: adding the head method to the list of convenience wrappers by @hiddenmarten in #760
- docs: update sponsors by @danielgtaylor in #764
- fix: do not HTML escape JSON output by default by @danielgtaylor in #765
- docs: fix model validator docs by @danielgtaylor in #766
- fix: better integer validation checks by @danielgtaylor in #768
- feat: better group+convenience function interop by @danielgtaylor in #770
New Contributors
- @hiddenmarten made their first contribution in #760
Full Changelog: v2.31.0...v2.32.0
v2.31.0
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:
- Start with all fields required.
- If a field has
omitempty, it is optional. - If a field has
omitzero, it is optional. - If a field has
required:"false", it is optional. - If a field has
required:"true", it is required.
See https://huma.rocks/features/request-validation/#optional-required for more info.
What's Changed
- Add 'omitzero' support to mark optional fields by @JasirZaeem in #742
- docs: fix typo in groups by @shadow3x3x3 in #746
New Contributors
- @JasirZaeem made their first contribution in #742
- @shadow3x3x3 made their first contribution in #746
Full Changelog: v2.30.0...v2.31.0
v2.30.0
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
- fix docs pointer by @iliadmitriev in #735
- feat: support context unwrapping by @danielgtaylor in #736
- feat: groups by @danielgtaylor in #728
- chore(deps): bump golang.org/x/net from 0.30.0 to 0.33.0 in /examples by @dependabot in #738
New Contributors
- @iliadmitriev made their first contribution in #735
Full Changelog: v2.29.0...v2.30.0
v2.29.0
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
Bodyfield is present.
What's Changed
- Update middleware.md by @NeroBlackstone in #710
- Update middleware.md by @NeroBlackstone in #712
- feat: allow arbitrary fields in MultipartFormFiles by @b-kamphorst in #706
- fix: double validation errors by @skwair in #718
- fix: implement findRelativeRessourcePath function to fix PatchResourc… by @froz42 in #714
- feat: custom query parameter types by @lsdch in #722
- fix: allow providing a custom request schema by @danielgtaylor in #727
- fix: do not panic when encountering external schemas by @danielgtaylor in #729
- fixed work with fiber.Ctx, fiber.UserContext - fixed graceful shutdown and race condition on access to huma.Context outside handler by @excavador in #725
- feat: add example for type renaming by @shakhzodkudratov in #731
- chore(deps): bump golang.org/x/net from 0.30.0 to 0.33.0 by @dependabot in #732
- feat: param query support deepObject style by @fourcels in #711
New Contributors
- @NeroBlackstone made their first contribution in #710
- @skwair made their first contribution in #718
- @froz42 made their first contribution in #714
- @shakhzodkudratov made their first contribution in #731
Full Changelog: v2.28.0...v2.29.0
v2.28.0
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
- chore(deps): bump golang.org/x/crypto from 0.28.0 to 0.31.0 by @dependabot in #676
- fix: useless assertion in TestFeatures/response-image by @alexandear in #677
- fix: potential body error race by @danielgtaylor in #687
- feat: upgrade to Stoplight Elements latest by @fourcels in #689
- fix: adapters/humafiber - prevent to use *fiber.Ctx and *fasthttp.RequestCtx outside handler, fixes #686 by @excavador in #693
- Fix examples by @alexandermyasnikov in #696
- docs: describe MultipartFormFiles by @b-kamphorst in #704
- refactor: split up huma.Register by @b-kamphorst in #705
New Contributors
- @alexandermyasnikov made their first contribution in #696
- @b-kamphorst made their first contribution in #704
Full Changelog: v2.27.0...v2.28.0