BYOR (Bring Your Own Router)#
BYOR (Bring Your Own Router)#
Huma is designed to be router-agnostic to enable incremental adoption in existing and new services across a large number of organizations. This means you can use any router you want, or even write your own. The only requirement is an implementation of a small huma.Adapter
interface. This is how Huma integrates with your router.
Adapters are in the adapters
directory and named after the router they support. Many common routers are supported out of the box (in alphabetical order):
- BunRouter via
humabunrouter
- chi via
humachi
- Echo via
humaecho
- Fiber via
humafiber
- gin via
humagin
- Go 1.22+
http.ServeMux
viahumago
(requiresgo 1.22
ingo.mod
) - gorilla/mux via
humamux
- httprouter via
humahttprouter
New Adapters
Writing your own adapter is quick and simple, and PRs are accepted for additional adapters to be built-in.
Chi Example#
Adapters are instantiated by wrapping your router and providing a Huma configuration object which describes the API. Here is a simple example using Chi:
import (
"github.com/danielgtaylor/huma/v2"
"github.com/danielgtaylor/huma/v2/adapters/humachi"
"github.com/go-chi/chi/v5"
)
// Create your router.
router := chi.NewMux()
// Wrap the router with Huma to create an API instance.
api := humachi.New(router, huma.DefaultConfig("My API", "1.0.0"))
// Register your operations with the API.
// ...
// Start the server!
http.ListenAndServe(":8888", r)
For existing services using Chi v4, you can use humachi.NewV4
instead.
Route Groups & Base URLs#
Many routers support grouping of operations under a common base URL path. This is useful for versioning APIs or grouping related operations together. Huma's router adapters can be instantiated using these route groups. You can set the OpenAPI().Servers
slice to include the base URL path for the group, enabling the correct URLs to be generated for the docs & schemas. Here is an example:
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)
The OpenAPI, docs, schemas, etc will all be generated with the /api
prefix.
# Call the demo operation
restish :8888/api/demo
# Get the OpenAPI
restish :8888/api/openapi.yaml
The following scenarios are supported:
Server Base URL | Route Group | Register Path | Client Request & Description |
---|---|---|---|
- | - | /api/demo |
GET /api/demo The default simple case. |
/api |
- | /demo |
GET /api/demo → GET /demo E.g. an API gateway which forwards requests to the service after stripping the /api prefix off the path. |
/api |
/api |
/demo |
GET /api/demo Unmodified request with route groups. |
Dive Deeper#
The adapter converts a router-specific request context like http.Request
or fiber.Ctx
into the router-agnostic huma.Context
, which is then used to call your operation's handler function.
graph LR
Request([Request])
OperationHandler[Operation Handler]
Request --> Router
Router -->|http.Request\nfiber.Ctx\netc| huma.Adapter
subgraph huma.API
huma.Adapter -->|huma.Context| OperationHandler
end
- Features
- Reference
huma.Context
a router-agnostic request/response contexthuma.Adapter
the router-agnostic adapter interfacehuma.API
the API instancehuma.NewAPI
creates an API instance (called by adapters)huma.Register
registers new operationshuma.OpenAPI
configures things like the server URLs & base path