Releases: danielgtaylor/huma
v2.13.1
Overview
This patch release fixes a small bug where a 204 No Content or 304 Not Modified which included a body would result in a panic. After this fix the body is ignored, making it easier to use the huma.Status304NotModified()
utility.
What's Changed
- feature: add Chinese document by @gg1229505432 in #385
- fix: do no try to write 204/304 body if present by @danielgtaylor in #390
New Contributors
- @gg1229505432 made their first contribution in #385
Full Changelog: v2.13.0...v2.13.1
v2.13.0
Overview
This release includes some minor but important changes that may break some existing users by removing deprecated functionality. I'm really sorry for the potential breaks and will do my best to adhere more strictly to semantic versioning in the future. Be aware that these changes are not taken lightly and possible alternatives were considered. I have also tried to group all the small breaks/changes into a single release to mitigate repeated painful upgrades. Building this type of library is hard and I feel for your frustrations and appreciate your understanding!
As of this release, you can now build a full-fledged Huma service with zero additional hard dependencies (if using the Go 1.22+ built-in huma.ServeMux
router and only JSON).
Removal of deprecated huma.NewCLI
Back in version 2.8.0 the huma.NewCLI
functionality was deprecated in favor of humacli.New
. The deprecated call is now removed.
Caution
This has the potential to break existing users, but is being treated as a bug fix to remove a hard dependency on spf13/cobra
. If you are already using humacli.New
there is no need to change anything.
Make CBOR support optional
CBOR is now made optional in the default config. If you wish to continue using it, you must opt-in to a new import, which automatically registers the format with the default configuration:
import (
"github.com/danielgtaylor/huma/v2"
_ "github.com/danielgtaylor/huma/v2/formats/cbor"
)
This new behavior is documented at https://huma.rocks/features/response-serialization/#default-formats. In the future this also makes it easier to support other additional formats without adding hard dependencies.
Warning
While not a breaking change per se, without adding the new import your clients will no longer be able to request CBOR and will get JSON responses instead.
humatest
no longer requires Chi
The humatest
package now uses a tiny internal router rather than relying on the Chi router, which introduced extra dependencies especially for people not using Chi as their main router package. You can continue to use humatest.Wrap
to wrap any router you like.
Also new are humatest.DumpRequest
/humatest.DumpResponse
and humatest.PrintRequest
/humatest.PrintResponse
utility functions which function similar to httptest.DumpRequest
/httptest.DumpResponse
but pretty print the body JSON to make debugging and showing examples easier. Usage example: https://go.dev/play/p/QQ9Bi7iws12.
Caution
This is technically a small breaking change, but is being treated as a bug fix to remove the dependency and make the package more future-proof by not relying on any single external router package and returning http.Handler
for the router instead.
Per-operation Middleware
You can now attach middleware to a huma.Operation
during registration. These middlewares will only run for that specific operation rather than for all operations:
huma.Register(api, huma.Operation{
Method: http.MethodGet,
Path: "/demo",
Middlewares: huma.Middlewares{
func(ctx huma.Context, next func(huma.Context)) {
// TODO: implement middleware here...
next(ctx)
},
},
}, func(ctx context.Context, input *struct{}) (*struct{}, error) {
// TODO: implement handler here...
return nil, nil
})
See also https://huma.rocks/features/middleware/#operations.
Add ctx.Status()
You can now get the response status from a huma.Context
in router-agnostic middleware, enabling easier logging/metrics/traces of the response status code.
func MyMiddleware(ctx huma.Context, next func(huma.Context)) {
next(ctx)
fmt.Println("Response status is", ctx.Status())
}
Better parameter descriptions
Some tools, notably SwaggerUI, require parameters to have descriptions to display properly (descriptions in the schema are not sufficient). These are now generated by Huma, so users opting to render docs with SwaggerUI should now get nicer output.
Bug Fixes
- The
humafiber
adapter accepts the correct group interface now. - Schema name text transformations for generics are now applied to hints as well as generated names
- Do not crash when
reflect.StructOf
fails in schema link transformer - Use
errors.As
when looking forhuma.StatusError
to enable error wrapping - Better error messages when missing OpenAPI config
What's Changed
- fix: NewWithGroup accepting fiber.Router by @barklan in #361
- fix: remove deprecated cli functionality to limit dependencies by @danielgtaylor in #363
- fix: apply schema name text transforms to type hints by @danielgtaylor in #367
- fix: vendor casing library by @danielgtaylor in #368
- fix: do not require Chi for humatest by @danielgtaylor in #370
- Add the ability to get ctx.Status() by @ssoroka-tc in #369
- feat: add param descriptions from schemas by @danielgtaylor in #374
- fix: catch reflect.StructOf panics in schema transformer by @danielgtaylor in #375
- fix: use errors.As to get StatusError, enabling wrapping by @danielgtaylor in #381
- feat: add per-operation router-agnostic middleware by @danielgtaylor in #382
- must specify openapi config by @ssoroka in #383
- fix: make cbor support optional by @danielgtaylor in #372
New Contributors
- @barklan made their first contribution in #361
- @ssoroka-tc made their first contribution in #369
- @ssoroka made their first contribution in #383
Full Changelog: v2.12.0...v2.13.0
v2.12.0
Overview
Support json.RawMessage
There is now better support in the validator for json.RawMessage
, which turns into an empty schema (allow anything).
type Demo struct {
Field1 string `json:"field1"`
Field2 json.RawMessage `json:"field2"`
}
OpenAPI 3.0.3
It's now easy to get OpenAPI 3.0.3 from a Huma service for tools that aren't completely compatible with OpenAPI 3.1 just yet. See https://huma.rocks/features/openapi-generation/. The new specs are available at /openapi-3.0.json
and /openapi-3.0.yaml
by default. Programmatic access is available via api.OpenAPI().Downgrade()
.
Better Support for Optional / Nullable Types
Huma now has better support for optional/nullable schemas. Broadly speaking, omitempty
controls whether a field is optional/required and the use of a pointer controls nullability. As described in:
- https://huma.rocks/features/request-validation/#optional-required
- https://huma.rocks/features/request-validation/#nullable
Huma generates type arrays from pointers to simple scalar types (boolean
, integer
, number
, string
) like "type": ["string", "null"]
. This is especially useful for frontend languages like Javascript/Typescript where there is a distinction between explicit null vs. undefined and an SDK generator might produce a type union like:
// Generated Typescript example
type GreetingOutputBody = {
message: string | null;
};
In addition to the default automatic behavior for determining optional/nullable, these can be manually overridden by the user via the required:"true|false"
and nullable:"true|false"
field tags, ensuring you have full control over how the schema is generated.
Important
Types which result in a JSON array
or object
are not marked as nullable by default, due to the complexity of modeling this in JSON Schema and poor support from SDK generators for Go and some other languages. This may change in a future release. You can mark a struct as nullable manually using a _
field with a nullable:"true"
tag as described in the docs linked above.
What's Changed
- docs: add sponsors to docs homepage by @danielgtaylor in #349
- feat: support json.RawMessage by @danielgtaylor in #350
- docs: fixed typo in reference docs by @brahmlower in #354
- feat: better support for optional/nullable types by @danielgtaylor in #351
- docs: fix middleware header example by @danielgtaylor in #358
New Contributors
- @brahmlower made their first contribution in #354
Full Changelog: v2.11.0...v2.12.0
v2.11.0
Overview
Sponsors
Big shout out to our new sponsors. Thank you so much! ❤️
Stoplight Elements 8.1.0
Stoplight Elements is upgraded to the latest release, version 8.1.0.
Fix omitempty
Without Name
Providing a JSON tag without a name but with ,omitempty
would result in fields without a name. This is now fixed and uses the same behavior as encoding/json
.
type Demo struct {
Field string `json:",omitempty"`
}
The field name would then be Field
in the JSON.
Route Groups & Servers Base Path
There is now better support for route groups in various routers, as well as smart routing for fetching the OpenAPI / JSON schemas when the OpenAPI servers
field is set with a base path.
mux := chi.NewMux()
mux.Route("/api", func(r chi.Router) {
config := huma.DefaultConfig("My API", "1.0.0")
config.Servers = []*huma.Server{
{URL: "https://example.com/api"},
}
api = humachi.New(r, config)
// Register operations...
huma.Get(api, "/demo", func(ctx context.Context, input *struct{}) (*struct{}, error) {
// TODO: Implement me!
return nil, nil
})
})
http.ListenAndServe("localhost:8888", mux)
More docs at https://huma.rocks/features/bring-your-own-router/#route-groups-base-urls.
Schema Field Example
The generated $schema
field now shows an example in the docs to help make it easy to see the JSON schema path on the server.
Docs Page Title
If an OpenAPI title
is provided, then the docs page title will use it now.
What's Changed
- docs: add sponsors; add dark mode images by @danielgtaylor in #335
- docs: API documentation customization by @danielgtaylor in #336
- feat: upgrade to Stoplight Elements 8.1.0 by @danielgtaylor in #339
- fix: update docs page copyright to 2024 by @danielgtaylor in #340
- fix: support fields with ,omitempty but no name by @danielgtaylor in #341
- feat: support OpenAPI servers base path & router groups by @danielgtaylor in #338
- feat: add example to $schema links by @danielgtaylor in #343
- feat: use config.Info.Title as the page title in the API docs by @nstapelbroek in #346
New Contributors
- @nstapelbroek made their first contribution in #346
Full Changelog: v2.10.0...v2.11.0
v2.10.0
Overview
Sponsors
Big shout out to our new sponsors. Thank you so much! ❤️
Better Support For Recursive Structs
Request/response bodies now have better support for recursive data structures, for example:
type Node struct {
Value string `json:"value"`
Left *Node `json:"left,omitempty"`
Right *Node `json:"right,omitempty"`
}
Multipart Form Support
You can now more easily access the incoming request's multipart form via the RawBody
field:
huma.Register(api, huma.Operation{
OperationID: "upload-files",
Method: http.MethodPost,
Path: "/upload",
Summary: "Example to upload a file",
}, func(ctx context.Context, input struct {
RawBody multipart.Form
}) (*struct{}, error) {
// Process multipart form here.
return nil, nil
})
https://huma.rocks/features/request-inputs/#multipart-form-data
Body Fields Can Be Hidden
You can now hide body fields just like you could e.g. query/header params.
type MyObject struct {
Public string `json:"public"`
Private string `json:"private" hidden:"true"`
}
This is useful if you want the field hidden from the docs but still serialized on the wire, so json:"-"
would be inappropriate.
Type Aliases
Besides huma.SchemaProvider
, you can now override schema generation behavior by registering known types with aliases, making it possible to override types used in structs from other packages.
registry := huma.NewMapRegistry("#/components/schemas", huma.DefaultSchemaNamer)
registry.RegisterTypeAlias(reflect.TypeFor[some_external_pkg.OptionalStr](), reflect.TypeFor[string]())
registry.RegisterTypeAlias(reflect.TypeFor[some_external_pkg.OptionalDateTime](), reflect.TypeFor[time.Time]())
What's Changed
- feat: breaks infinite cycle when looking for fields in recursive structures. by @yuridevx in #306
- Multipart Form RawBody Type Support by @jamelt in #324
- feat: hidden body field support by @bekabaz in #328
- fix: allow multipart with body; prevent double status by @danielgtaylor in #329
- Allow specifying type aliases to provide schema definitions by @lsdch in #330
New Contributors
- @yuridevx made their first contribution in #306
- @jamelt made their first contribution in #324
- @bekabaz made their first contribution in #328
- @lsdch made their first contribution in #330
Full Changelog: v2.9.0...v2.10.0
v2.9.0
Overview
String Length Validation
String length now counts the UTF8 runes in a string rather than using len(value)
, making for a more accurate count of the visible characters and being compatible with systems supporting unicode.
Add dependentRequired
Validation
You can now utilize JSON Schema's dependentRequired
validation which marks a field as required conditional on the presence of another field, for example:
type PaymentInfo struct {
Name string `json:"name" doc:"Billing name"`
Card int64 `json:"card,omitempty" doc:"Credit card number" dependentRequired:"address"`
Address string `json:"address,omitempty" doc:"Billing address"`
IBAN string `json:"iban,omitempty" doc:"Bank account ID for transfer"`
}
This requires a name
and then you can pass an iban
for a bank transfer or use a credit card. If the credit card is passed, then validation will fail unless an address is also passed.
Readonly Nested Structs
Nested structs can now utilize the readOnly
/ writeOnly
/ etc validation:
type Example struct {
Field struct {
Value string `json:"value"`
} `readOnly:"true"`
}
Better Pattern Errors
String pattern validation using regular expressions can now provide a user-friendly name so that the error says something like expected string to be alphabetical
instead of expected string to match pattern ^[a-zA-Z]+$
.
type Example struct {
Field string `json:"field" pattern:"^[a-zA-Z]+$" patternDescription:"alphabetical"`
}
Overriding Fields
A bug was fixed in the generated JSON Schema when overriding fields:
type One struct {
A string `json:"a"`
B string `json:"b"`
}
type Two struct {
One
B string `json:"-"`
}
This will result in a JSON Schema for Two
with only one field: a
.
What's Changed
- fix: length validation for utf8 strings by @maximdanilchenko in #298
- fix: issue 299 by @victoraugustolls in #304
- feat: dependent required tag by @victoraugustolls in #303
- chore(deps): bump google.golang.org/protobuf from 1.32.0 to 1.33.0 by @dependabot in #308
- chore(deps): bump google.golang.org/protobuf from 1.32.0 to 1.33.0 in /examples by @dependabot in #309
- fix: make dependent error deterministic by @danielgtaylor in #310
- feat: support dep required for map[any]any by @danielgtaylor in #312
- fix: authorizationUrl is only required for implicit/authcode by @danielgtaylor in #318
- doc: Add sponsor/funding info by @danielgtaylor in #319
- fix: skip param required check if SkipValidateParams is set by @danielgtaylor in #320
- Nested struct visibility by @CptKirk in #314
- feat(validation): flexible validation messages for pattern tag by @purin-dev in #323
New Contributors
- @maximdanilchenko made their first contribution in #298
- @victoraugustolls made their first contribution in #304
- @CptKirk made their first contribution in #314
- @purin-dev made their first contribution in #323
Full Changelog: v2.8.0...v2.9.0
v2.8.0
Overview
CLI Package
The CLI functionality has been moved into its own package humacli
. The existing huma.NewCLI
call continues to work but is marked as deprecated and will be removed in a future release. This will be a small breaking change in the future but is necessary to fix a design mistake that impacts dependencies that cannot otherwise be resolved. Migrating is an easy find/replace:
huma.NewCLI
→humacli.New
huma.CLI
→humacli.CLI
huma.Hooks
→humacli.Hooks
huma.WithOptions
→humacli.WithOptions
If you want to ensure your code doesn't include anything from the existing CLI and Cobra functionality, use the humanewclipackage
build tag, e.g. go build -tags humanewclipackage
. You won't save much space but this can help for package auditing.
Convenience Summary
Convenience functions like huma.Get
, huma.Put
, etc now generate a human-readable summary of the operation using the path. For example, huma.Get(api, "/things/{id}", ...)
would generate a summary of Get things by id
.
Easier Context Values
Router-agnostic middleware has gotten a bit easier to write with the new huma.WithValue
and huma.WithContext
functions to return a wrapped Huma context with its underlying request context.Context
replaced. Use it like:
func MyMiddleware(ctx huma.Context, next func(huma.Context)) {
// Wrap the context to add a value.
ctx = huma.WithValue(ctx, "some-key", "some-value")
// Call the next middleware in the chain. This eventually calls the
// operation handler as well.
next(ctx)
}
What's Changed
- docs: remove experimental flag for Go 1.22+ by @danielgtaylor in #282
- docs: fix broken link by @danielgtaylor in #283
- feat: add convenience functions for summary by @xarunoba in #284
- feat: add WithContext and WithValue for setting context values by @danielgtaylor in #295
- fix: move CLI to its own package, deprecate huma.NewCLI by @danielgtaylor in #291
New Contributors
Full Changelog: v2.7.0...v2.8.0
v2.7.0
New Features
Convenience Functions
Convenience functions are available for common HTTP verbs, e.g. huma.Get
, huma.Post
, huma.Put
, etc. These provide less control over the OpenAPI generation, but are significantly less verbose than huma.Register
and make it easier to get started, provide quick examples/demos, and more.
huma.Get(api, "/demo", func(ctx context.Context, input *Input) (*Output, error) {
// ...
})
The OpenAPI operationId
field is generated from the path. The behavior can be modified by overriding huma.GenerateOperationID
if desired. It's easy to switch to huma.Register
at any time if/when you want to provide more information for the OpenAPI generation.
Custom Input Params
You can now use any type that supports encoding.TextUnmarshaler
as an input param (path/query/header/cookie). Combined with custom field schemas this is very powerful, and it can use custom request resolvers as well enabling better support for exhaustive error responses to clients. For example, the Google UUID library supports TextUnmarshaler
:
import "github.com/google/uuid"
type UUID struct {
uuid.UUID
}
func (UUID) Schema(r huma.Registry) *huma.Schema {
return &huma.Schema{Type: huma.TypeString, Format: "uuid"}
}
huma.Get(api, "/demo", func(ctx context.Context, input *struct{
Example UUID `query:"example"`
}) (*Output, error) {
// Print out the UUID time portion
fmt.Println(input.Example.Time())
})
What's Changed
- fix: set CLI name from executable, add docs for name/version by @danielgtaylor in #265
- fix: Set body height to 100% of the viewport height by @mailbaoer in #271
- feat: support input param types using
TextUnmarshaler
by @danielgtaylor in #276 - fix: allow response types to contain unexported fields by @FlorianLoch in #278
- feat: add convenience methods by @danielgtaylor in #279
- fix: return wrapped errors on transform or write failure by @danielgtaylor in #280
New Contributors
- @mailbaoer made their first contribution in #271
- @FlorianLoch made their first contribution in #278
Full Changelog: v2.6.0...v2.7.0
v2.6.0
What's Changed
- chore(deps): bump github.com/gofiber/fiber/v2 from 2.52.0 to 2.52.1 by @dependabot in #261
- feat: better support for cookies by @danielgtaylor in #258
Full Changelog: v2.5.0...v2.6.0
v2.5.0
What's Changed
- fix: resolver should never be invoked twice by @danielgtaylor in #235
- docs: adds echo router to BYOR page by @certanet in #239
- fix: allow example/enum tags for custom schemas by @danielgtaylor in #242
- docs: README updates & RFC 9457 errors by @danielgtaylor in #244
- feat: support time.Duration options by @danielgtaylor in #245
- fix: do not generate json response for binary outputs by @danielgtaylor in #247
- docs: add doc about middleware errors by @danielgtaylor in #248
- docs: improve docs for unstructured request bodies by @danielgtaylor in #251
- change elements try it credentials policy to same-origin by @paivagustavo in #254
- feat: better control over object additionalProperties by @danielgtaylor in #256
New Contributors
- @certanet made their first contribution in #239
- @paivagustavo made their first contribution in #254
Full Changelog: v2.4.0...v2.5.0