Releases: danielgtaylor/huma
v2.23.0
Overview
Pointers for Non-Param Fields
It's now possible to use pointers for non-param fields in input structs without Huma complaining. For example, here the User
is not a path/query/header param and is populated from the Authorization
header value for use later:
type EndpointInput struct {
Token string `header:"Authorization"`
User *User
}
func (i *EndpointInput) Resolve(ctx huma.Context) []error {
user, token_valid := ValidateToken(i.Token) // user is nil if token is missing or invalid
i.User = user
return nil
}
Hidden Field Validation
Hidden fields are now validated properly if they are present in the input. For example:
huma.Put(api, "/demo", func(ctx context.Context, input *struct{
Body struct {
Field1 string `json:"field1"
Field2 int `json:"field2" hidden:"true" minimum:"10"`
}
}) (*MyResponse, error) {
// If `input.Field2` is sent by the client, the request will fail
// if its value is below 10 due to the validation schema.
return &MyResponse{...}, nil
})
Prevent Overwriting Schema Validations
All validations now take the existing value of the validator as input when generating the schema, which means a SchemaProvider
or SchemaTransformer
output won't get overwritten when generating schemas. This fixes a bug that was partially fixed but missed several important fields like pattern
.
Non-Addressable Resolver
It's now possible to use non-addressable types which implement Resolver
, such as custom primitive types as map keys. This is currently a little less efficient as a pointer to the type needs to be generated, but at least it is now possible and performance can be improved in the future.
Use the Status Code from NewError
When providing your own custom huma.NewError
function, the resulting error's status code was ignored. This has been fixed to be used as the output status code, enabling the function to modify the status code before going out on the wire.
NewError with a Context
It's now possible to replace huma.NewErrorWithContext
so your error generation function has access to the underlying request context.
NewWithPrefix & Servers
When using humago.NewWithPrefix
and not providing any servers, a single server entry is now generated for you with the given prefix.
Support url.URL
Parameters
You can now use a URL as an input path/query/header parameter and it will be parsed/validated for you.
Request Body Generation Improvements
Like response body generation, the request body generation has been improved to generate missing pieces of the body OpenAPI structure. This enables you to easily e.g. add a description but have Huma still generate the JSON Schema for you. Example:
func (tr TEERouter) RegisterRoutes(api huma.API) {
operation := huma.Operation{
Method: http.MethodPost,
Path: "/tee",
Summary: "TEE",
Description: "TEE description",
RequestBody: &huma.RequestBody{
Description: "My custom request schema",
},
}
huma.Register(api, operation, tr.CalculateTEE)
}
What's Changed
- Allow using pointers for non param fields in input structs by @lsdch in #565
- fix: validate hidden fields by @danielgtaylor in #573
- fix: prevent overwriting schema validations by @danielgtaylor in #575
- fix: support non-addressable resolver values by @danielgtaylor in #580
- fix: use status code returned from NewError when writing errors by @danielgtaylor in #581
- add NewErrorWithContext by @nunoo in #582
- Add default Server object when calling humago.NewWithPrefix by @yursan9 in #579
- fix: unsupported param type url.URL by @ddl-ebrown in #584
- fix: allow setting request description and still gen schema by @danielgtaylor in #587
New Contributors
- @yursan9 made their first contribution in #579
- @ddl-ebrown made their first contribution in #584
Full Changelog: v2.22.1...v2.23.0
v1.14.3
What's Changed
- fix: reset recovery middleware buffer by @danielgtaylor in #572
Full Changelog: v1.14.2...v1.14.3
v2.22.1
Overview
This patch release fixes a bug where the order of operations when resetting a buffer could cause a race condition when putting that buffer back into the shared sync.Pool
for re-use when reading in request bodies.
What's Changed
- fix: order of operations when resetting buffer by @danielgtaylor in #553
Full Changelog: v2.22.0...v2.22.1
v2.22.0
Sponsors
A big thank you to our new sponsor:
Overview
Minimum Go Version: 1.21
The minimum Go version has been upgraded to 1.21, in alignment with the official Go policy. This enables us to fix some critical vulnerabilities with optional dependencies via dependabot and allows the code to be updated to use newer packages like slices
, modernizing the codebase.
Each major Go release is supported until there are two newer major releases. For example, Go 1.5 was supported until the Go 1.7 release, and Go 1.6 was supported until the Go 1.8 release.
https://go.dev/doc/devel/release
Fixes Raw Body Race Condition
This release fixes a critical bug where you could run into a race condition using a shared buffer when accessing a request's RawBody []byte
field. The buffer was getting returned to the sync.Pool
too early, resulting in multiple requests having concurrent access. For handlers which register needing access to the RawBody
field, returning the buffer to the pool is now deferred until after then entire handler has run, fixing the issue.
Warning
If you use the RawBody
feature, you should upgrade immediately. This bug results in incorrect/corrupted data.
Better encoding.TextUnmarshaler
Support
Support for types which implement encoding.TextUnmarshaler
has been improved. The types are now treated as a JSON Schema string
by default, making it easier to set up validation and defaults without needing to provide a custom schema via huma.SchemaProvider
. Among other things this can be used for custom date/time types:
type MyDate time.Time
func (d *MyDate) UnmarshalText(data []byte) error {
t, err := time.Parse(time.RFC3339, string(data))
if err != nil {
return err
}
*d = MyDate(t)
return nil
}
// Later use it in a request
type Request struct {
Date MyDate `json:"date" format:"date-time" example:"2024-01-01T12:00:00Z"`
}
Precompute Schema Validation
Schema validation messages are no longer required to be precomputed manually with a call to schema.PrecomputeMessages()
as this now happens at operation registration time. This simplifies using custom schemas and makes it possible to define them inline with the operation.
If you modify a schema after registration, you must still call PrecomputeMessages()
manually to update the messages.
Fix Nil Response Panic
If an operation is registered as returning a body and a handler mistakenly invokes return nil, nil
(meaning no response, no error) this caused a panic as the body is required. This release changes that behavior to no longer panic, but instead return the operation's default status code instead.
What's Changed
- fix: race by deferring the return of buf to sync.Pool when using RawBody by @nunoo in #542
- fix: automatically precompute schema validation messages by @danielgtaylor in #545
- fix: if err & response are nil, return default status by @danielgtaylor in #546
- feat: Update minimum Go version to 1.21 by @danielgtaylor in #547
- chore(deps): bump github.com/gofiber/fiber/v2 from 2.52.1 to 2.52.5 by @dependabot in #549
- feat: treat encoding.TextUnmarshaler as string in schema by @danielgtaylor in #550
New Contributors
Full Changelog: v2.21.0...v2.22.0
v2.21.0
Overview
Better Support for Default/Example in Custom Schemas
Fixes an issue where custom schemas could have values overwritten by the default instead of using the given value. For example:
type GreetingType int
func (*GreetingType) Schema(r huma.Registry) *huma.Schema {
schema := &huma.Schema{
Type: huma.TypeInteger,
Default: 10,
Examples: []any{1},
}
return schema
}
Better Errors When Using Discriminators
OpenAPI supports using a discriminator field in schemas that use oneOf
to determine which of the included schemas to validate against. Huma now uses this information to generate better error messages like expected required property color to be present
instead of just saying it expected one of the schemas to match. Also handles problems with the discriminator type and value mapping. Example https://go.dev/play/p/5gkNczNJ_jK:
type Cat struct {
Name string `json:"name" minLength:"2" maxLength:"10"`
Kind string `json:"kind" enum:"cat"`
}
type Dog struct {
Color string `json:"color" enum:"black,white,brown"`
Kind string `json:"kind" enum:"dog"`
}
type DogOrCat struct {
Kind string `json:"kind" enum:"cat,dog"`
}
func (v DogOrCat) Schema(r huma.Registry) *huma.Schema {
catSchema := r.Schema(reflect.TypeOf(Cat{}), true, "Cat")
dogSchema := r.Schema(reflect.TypeOf(Dog{}), true, "Dog")
return &huma.Schema{
Type: huma.TypeObject,
Description: "Animal",
OneOf: []*huma.Schema{
{Ref: catSchema.Ref},
{Ref: dogSchema.Ref},
},
Discriminator: &huma.Discriminator{
PropertyName: "kind",
Mapping: map[string]string{
"cat": catSchema.Ref,
"dog": dogSchema.Ref,
},
},
}
}
// ...
huma.Put(api, "/demo", func(ctx context.Context, input *struct {
Body DogOrCat
}) (*DemoResponse, error) {
resp := &DemoResponse{}
resp.Body.Message = "You sent a " + input.Body.Kind
return resp, nil
})
What's Changed
- fix(schema): default value not work by @fourcels in #531
- fix(typo): accmplished -> accomplished by @superstas in #534
- fix(schema): schema example not work in Parameter by @fourcels in #532
- #533: Add Validation for Discriminator with OneOf and Mapping. by @superstas in #536
Full Changelog: v2.20.0...v2.21.0
v2.20.0
Overview
Sponsors
Thank you to Zuplo for sponsoring the project.
Fixed Validation Tags & Referenced Schemas
Sometimes schemas and schemas in references wound up getting overwritten with default values for validation like minimum
, maximum
, minLength
, maxLength
, as well as skipping calling TransformSchema
. This is now fixed to behave as expected by derefencing structs and only setting validation values when the tag is actually present.
Customizable Validation Errors
Validation errors are now completely customizable as globals in the library.
// Set a custom message for minimum validation failure.
huma.MsgExpectedMinimumNumber = "expected number bigger than or equal to %v"
You can also change the function used to format error messages via huma.ErrorFormatter
. This must be overwritten before generating any schemas, as the generated messages are cached at schema creation time.
var ErrorFormatter func(format string, a ...any) string = fmt.Sprintf
Nullable Slices
Due to the way nil
slices get encoded in Go as null
in JSON and other formats, we now generate array schemas as nullable by default. See also the discussion and links to JSON v2 at golang/go#37711.
What's Changed
- fix(schema): proper minLength, maxLength in generated schemas. by @superstas in #513
- fix(schema): proper minimum, maximum in generated schemas. by @superstas in #515
- fix(schema): add dereferencing for SchemaFromType by @superstas in #518
- feat: add zuplo sponsorship by @danielgtaylor in #501
- docs: fix formatting in sponsors by @danielgtaylor in #523
- feat: make validation error messages customizable by @smacker in #520
- fix: nullable schemas for arrays/slices by @lsdch in #527
New Contributors
- @superstas made their first contribution in #513
- @smacker made their first contribution in #520
Full Changelog: v2.19.0...v2.20.0
v2.19.0
Overview
Sponsors
A big thank you to our new sponsor:
Multipart Form File Metadata
It's now possible to get filename & size metadata information from multipart form files.
huma.Post(api, "/form-example", func(ctx context.Context, input *struct{
RawBody huma.MultipartFormFiles[struct {
HelloWorld huma.FormFile `form:"file" contentType:"text/plain" required:"true"`
}]
}) (*struct{}, error) {
fileData := input.RawBody.Data()
fmt.Println( fileData.HelloWorld.Filename)
fmt.Println( fileData.HelloWorld.Size)
}
Easier Custom Context When Testing
It's now easier to pass a custom context to operations in the test API. Instead of having to create a custom request with its own context and manually call the adapter you can now use the methods like GetCtx
instead of Get
.
_, api := humatest.New(t)
ctx := context.Background() // define your necessary context
resp := api.GetCtx(ctx, "/greeting/world") // provide it using the 'Ctx' suffixed methods
Exploded Query Params
It's now possible to use the OpenAPI explode
feature where query params are passed multiple times rather than using comma separated values.
huma.Get(api, "/example", func(ctx context.Context, input *struct{
Value []string `query:"value,explode"`
}) (*struct{}, error) {
fmt.Println(input.Value)
return nil, nil
})
You can then make requests like GET /example?value=foo&value=bar
.
Autopatch Schema Improvements
Autopatch now uses the PUT
schema (modified to all be optional) rather than just relying on an object
with any allowed properties, which improves documentation for users. This is automatic so there is no need to configure anything new.
Other Improvements
- Performance improvement by removing the response body from panics which could be very large.
- Fixes to min/max items schema generation when using arrays.
- Remove
slices
dependency to continue to support Go 1.20 until 1.23 is released (we will support the latest two major versions just like the Go project itself)
What's Changed
- fix: expose metadata of decoded multipart form files by @lsdch in #466
- Extend TestAPI interface to allow for custom context.Context by @coury-clark in #469
- Update README_CN.md by @Ivlyth in #477
- remove response body from panic message by @austincollinpena in #479
- fix(schema): proper array minItems, maxItems and doc reporting in generated schemas by @Grumpfy in #485
- fix: remove slices dependency to better support Go 1.20 by @danielgtaylor in #497
- Implement exploded query parameters by @csmarchbanks in #498
- Improvement Autopatch (adding a body) by @ScriptType in #496
- docs: add new sponsor by @danielgtaylor in #506
New Contributors
- @coury-clark made their first contribution in #469
- @Ivlyth made their first contribution in #477
- @Grumpfy made their first contribution in #485
- @csmarchbanks made their first contribution in #498
- @ScriptType made their first contribution in #496
Full Changelog: v2.18.0...v2.19.0
v2.18.0
Overview
Better Multipart Files Uploads
You can now use huma.MultipartFormFiles[YourType]
as the request's RawBody
in order to handle multipart file uploads. Files can be limited to specific content types and required or optional. Example:
type FileData struct {
// This is an example, any number of `multipart.File` fields can be defined.
// Nested structs are not supported.
SomeFile multipart.File `form-data:"some_file" content-type:"image/png" required:"true"`
SeveralFiles []multipart.File `form-data:"several_files" content-type:"image/png,image/jpeg" required:"true"`
}
type FileHandlerInput struct {
RawBody huma.MultipartFormFiles[FileData]
}
func FileHandler(ctx context.Context, input *FileHandlerInput) (*struct{}, error) {
fileData := input.RawBody.Data()
DoSomeThingWith(fileData.SomeFile)
OrSomethingElseWith(fileData.SeveralFiles)
}
huma.Register(api,
huma.Operation{
Path: "/handle-files",
Method: http.MethodPost,
OperationID: "Handle files",
}, FileHandler)
Schema Transformers
It's now possible to have types implement a schema transformer, which lets them modify the generated schema for the type. This option lives in between using the generated types and providing your own schema, and makes it a bit easier to modify the generated schema by not needing you to call into the registry manually. This is the interface:
type SchemaTransformer interface {
TransformSchema(r Registry, s *Schema) *Schema
}
Simple example:
type MyInput struct {
Field string `json:"field"`
}
func (i *MyInput) TransformSchema(r huma.Registry, s *huma.Schema) *huma.Schema {
s.Description = "I am an override"
return s
}
What's Changed
- Fix typo in SSE doc by @op-tmplt in #448
- Feature: handling files from
multipart/form-data
request by @lsdch in #415 - feat: allow modifying generated schema by implementing SchemaTransformer interface by @lsdch in #456
- fix: type schema description not work by @fourcels in #462
New Contributors
Full Changelog: v2.17.0...v2.18.0
v2.17.0
Overview
Convenience Method Operation Modifiers
You can now add modifier functions when registering operations using convenience methods like huma.Get
:
func OperationTags(tags ...string) func(o *Operation) {
return func(o *Operation) {
o.Tags = tags
}
}
huma.Get(api, "/demo", myHandler, OperationTags("one", "two"))
Use this to build out whatever functionality you need to simplify registration. These can also be composed & joined easily to give a concise way to register operations without falling back to huma.Register
directly.
Request Remote Address Access
It's now possible to access the requests's RemoteAddr
field through huma.Context
for use in middleware and/or resolvers, which works with all routers:
func MyMiddleware(ctx huma.Context, next func(huma.Context)) {
fmt.Println("Request address", ctx.RemoteAddr())
next(ctx)
}
Fix Unmarshaling of Embedded Body
Unmarshaling of embedded structs with a body field now works as expected:
type BodyContainer struct {
Body struct {
Name string `json:"name"`
}
}
huma.Register(api, huma.Operation{
Method: http.MethodPost,
Path: "/body",
}, func(ctx context.Context, input *struct {
BodyContainer
}) (*struct{}, error) {
// Do something with `input.Body.Name`...
return nil, nil
})
What's Changed
- feat: convenience methods with operationHandlers by @fourcels in #434
- fix: race condition in CLI tests by @danielgtaylor in #436
- docs: add new sponsor and quote by @danielgtaylor in #437
- fix: unmarshaling of request body when using struct embedding by @lsdch in #440
- feat: add request remote address to context by @danielgtaylor in #447
New Contributors
Full Changelog: v2.16.0...v2.17.0
v2.16.0
Overview
Drop Deprecated Chi v4 Adapter
This release drops Chi v4 support. Chi v4 has been deprecated & replaced with Chi v5 since 2021, and was included to help migrate from Huma v1 to v2. Huma v2 has now been out for about six months. Keeping Chi v4 in the main package results in extra dependencies and deprecation warnings from automated tools like dependabot. It is highly recommended to discontinue using Chi v4.
If you wish to continue using Chi v4, please copy the v4 adapter from https://github.com/danielgtaylor/huma/blob/v2.15.0/adapters/humachi/humachi.go into your own project. With that simple change you can migrate off at your own pace while continuing to get new Huma releases.
Better Panics
Some panic messages have been improved to be more helpful for users, for example letting them know that the response type must be a struct rather than some confusing reflection panic.
What's Changed
- feat!: drop chi v4 adapter by @costela in #427
- panic with more descriptive messages by @ssoroka in #431
Full Changelog: v2.15.0...v2.16.0