Skip to content

Releases: danielgtaylor/huma

v2.10.0

25 Mar 15:51
38cf30c
Compare
Choose a tag to compare

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

Full Changelog: v2.9.0...v2.10.0

v2.9.0

18 Mar 16:02
c312351
Compare
Choose a tag to compare

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

New Contributors

Full Changelog: v2.8.0...v2.9.0

v2.8.0

11 Mar 16:34
13acb69
Compare
Choose a tag to compare

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.NewCLIhumacli.New
  • huma.CLIhumacli.CLI
  • huma.Hookshumacli.Hooks
  • huma.WithOptionshumacli.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

New Contributors

Full Changelog: v2.7.0...v2.8.0

v2.7.0

05 Mar 06:07
v2.7.0
aa2e3a7
Compare
Choose a tag to compare

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

New Contributors

Full Changelog: v2.6.0...v2.7.0

v2.6.0

26 Feb 16:14
v2.6.0
95a3021
Compare
Choose a tag to compare

What's Changed

Full Changelog: v2.5.0...v2.6.0

v2.5.0

20 Feb 17:01
v2.5.0
15f15fe
Compare
Choose a tag to compare

What's Changed

New Contributors

Full Changelog: v2.4.0...v2.5.0

v2.4.0

04 Feb 17:38
v2.4.0
42461d5
Compare
Choose a tag to compare

What's Changed

New Contributors

Full Changelog: v2.3.0...v2.4.0

v2.3.0

25 Jan 18:02
v2.3.0
c212880
Compare
Choose a tag to compare

What's Changed

New Contributors

Full Changelog: v2.2.0...v2.3.0

v2.2.0

06 Jan 01:00
v2.2.0
1d337f1
Compare
Choose a tag to compare

What's Changed

New Contributors

Full Changelog: v2.1.0...v2.2.0

v2.1.0

13 Dec 17:55
v2.1.0
8f08975
Compare
Choose a tag to compare

What's Changed

New Contributors

Full Changelog: v2.0.1...v2.1.0