import "github.com/struct0x/hx"- func Bind[T any](r *http.Request, dst *T, opts ...BindOpt) error
- func BindProblem(err error, summary string, opts ...ProblemOpt) error
- func HijackResponseWriter(ctx context.Context) http.ResponseWriter
- type BindOpt
- type Documented
- type Field
- type HX
- func New(opts ...Opt) *HX
- func (h *HX) Group(prefix string, mids ...Middleware) *HX
- func (h *HX) Handle(pattern string, handler Handler, mids ...Middleware)
- func (h *HX) HandleFunc(pattern string, handler HandlerFunc, opts ...RouteOpt)
- func (h *HX) Routes() []RouteInfo
- func (h *HX) ServeHTTP(w http.ResponseWriter, r *http.Request)
- type Handler
- type HandlerFunc
- type Middleware
- type Mux
- type Opt
- type ProblemDetails
- func BadRequest(title string, opts ...ProblemOpt) ProblemDetails
- func Conflict(title string, opts ...ProblemOpt) ProblemDetails
- func Forbidden(title string, opts ...ProblemOpt) ProblemDetails
- func Internal(title string, opts ...ProblemOpt) ProblemDetails
- func MethodNotAllowed(title string, opts ...ProblemOpt) ProblemDetails
- func NotFound(title string, opts ...ProblemOpt) ProblemDetails
- func Problem(status int, summary string, opts ...ProblemOpt) ProblemDetails
- func Unauthorized(title string, opts ...ProblemOpt) ProblemDetails
- type ProblemOpt
- type Response
- type ResponseOpt
- type RouteDoc
- type RouteInfo
- type RouteOpt
func Bind
func Bind[T any](r *http.Request, dst *T, opts ...BindOpt) errorBind extracts data from an HTTP request into a destination struct. It supports binding from multiple sources including URL query parameters, path variables, headers, cookies, JSON body, and multipart file uploads.
The destination must be a pointer to a struct. Fields in the struct are bound based on struct tags that specify the data source and field name:
- `query:"name"` - binds from URL query parameters
- `path:"name"` - binds from URL path variables
- `header:"Name"` - binds from HTTP headers
- `cookie:"name"` - binds from HTTP cookies
- `json:"name"` - binds from JSON request body (application/json)
- `file:"name"` - binds file uploads from multipart/form-data
Supported field types include:
- Basic types: string, bool, int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64, float32, float64
- Slices of basic types (for multiple values)
- Slices of strings (for headers and query parameters with multiple values)
- http.Cookie (for cookie fields)
- multipart.FileHeader (for single file uploads)
- []*multipart.FileHeader (for multiple file uploads)
- Types implementing encoding.TextUnmarshaler
- Any type for json-tagged fields (unmarshaled via encoding/json)
Options:
- WithPathValueFunc: provides a custom function to extract path variables from the request. By default, uses http.Request.PathValue.
- WithMaxFormMemoryMB: sets the maximum memory in megabytes for parsing multipart forms. Defaults to 32 MB if not specified.
- WithValidator: sets a custom validator.Validate instance to be used for request validation. By default, a default validator is used.
Returns:
- nil if all fields are successfully bound
- error if any field fails to bind, use BindProblem to create a structured error response
Example usage:
type UserRequest struct {
ID int `path:"id"`
Name string `json:"name"`
Email string `json:"email"`
Tags []string `query:"tags"`
AuthToken string `header:"Authorization"`
}
func handler(ctx context.Context, r *http.Request) error {
var req UserRequest
if err := hx.Bind(r, &req); err != nil {
return BindProblem(err, "Invalid user request",
WithTypeURI("https://example.com/errors/invalid-user"))
}
// Use req...
return nil
}
func BindProblem
func BindProblem(err error, summary string, opts ...ProblemOpt) errorBindProblem creates a structured error response by wrapping binding errors into a ProblemDetails. It takes an error from Bind, a summary message, and optional ProblemOpt options.
The function handles different types of binding errors:
- Structural errors (nil request, nil destination, invalid types) return 500 Internal Server Error
- Validation errors return 400 Bad Request with detailed field errors in the extensions
- Other errors return 500 Internal Server Error
For validation errors, the response includes an "errors" field in the extensions containing an array of objects with "field" and "detail" properties for each validation error.
Allowed ProblemOpt options: - WithTypeURI sets the Type field of ProblemDetails. - WithDetail sets the Detail field of ProblemDetails. - WithField adds a single field to the Extensions map of ProblemDetails. - WithFields sets multiple fields at once. - WithInstance sets the Instance field of ProblemDetails. Note: WithCause option is automatically added and will be ignored if provided manually.
Example usage:
var req UserRequest
if err := Bind(r, &req); err != nil {
return BindProblem(err, "Invalid user request",
WithTypeURI("https://example.com/errors/invalid-user"))
}
func HijackResponseWriter
func HijackResponseWriter(ctx context.Context) http.ResponseWriterHijackResponseWriter retrieves the http.ResponseWriter from the context. When the ResponseWriter is hijacked, the return value from HandlerFunc will be ignored.
type BindOpt
type BindOpt = bind.Optfunc WithMaxFormMemoryMB
func WithMaxFormMemoryMB(maxFormMemoryMB int64) BindOptWithMaxFormMemoryMB configures the maximum size of multipart form data that will reside in memory. The rest of the data will be stored on disk in temporary files. This option is used when binding multipart form data and file uploads.
func WithPathValueFunc
func WithPathValueFunc(fn func(r *http.Request, name string) string) BindOptWithPathValueFunc overrides the default way of extracting a path parameter from the request. The function receives the request and the name of the path variable and must return the value (or the empty string if the variable is not present).
func WithValidator
func WithValidator(v *validator.Validate) BindOptWithValidator configures a custom validator.Validate instance to be used for request validation. The validator will be used to validate struct fields with "validate" tags after binding. If not provided, a default validator will be used.
type Documented
type Documented interface {
Doc() RouteDoc
}type Field
Field represents a key-value pair that can be added to ProblemDetails extensions.
type Field struct {
Key string
Val any
}func F
func F(k string, v any) FieldF is a shorthand constructor for creating Field instances.
type HX
HX is a framework for building HTTP APIs with enhanced error handling and middleware support. It provides a convenient way to handle HTTP requests, manage middleware chains, and standardize error responses using ProblemDetails (RFC 9457).
Example usage:
hx := hx.New(
hx.WithLogger(slog.Default()),
hx.WithCustomMux(http.NewServeMux()),
hx.WithMiddleware(loggingMiddleware),
)
// HandleFunc requests
hx.HandleFunc("/api/users", func(ctx context.Context, r *http.Request) error {
// HandleFunc the request
return nil
})
// Start the server
http.ListenAndServe(":8080", hx)
type HX struct {
// contains filtered or unexported fields
}func New
func New(opts ...Opt) *HXNew creates a new HX instance.
func (*HX) Group
func (h *HX) Group(prefix string, mids ...Middleware) *HXGroup creates a sub-router sharing the same mux, with the given path prefix and additional middlewares appended to the current chain.
Example:
api := server.Group("/api/v1", authMiddleware)
api.HandleFunc("POST /users", createUserHandler) // registers "POST /api/v1/users"
api.HandleFunc("/orders", listOrdersHandler) // registers "/api/v1/orders"
admin := api.Group("/admin", adminOnlyMiddleware)
admin.HandleFunc("/stats", statsHandler) // registers "/api/v1/admin/stats"
func (*HX) Handle
func (h *HX) Handle(pattern string, handler Handler, mids ...Middleware)func (*HX) HandleFunc
func (h *HX) HandleFunc(pattern string, handler HandlerFunc, opts ...RouteOpt)HandleFunc registers a new request handler with the given pattern and route options. Options can be Middleware values or a Doc describing the route for spec generation.
func (*HX) Routes
func (h *HX) Routes() []RouteInfoRoutes returns all routes registered on this server, including those registered via groups.
func (*HX) ServeHTTP
func (h *HX) ServeHTTP(w http.ResponseWriter, r *http.Request)type Handler
type Handler interface {
ServeHX(ctx context.Context, r *http.Request) error
}type HandlerFunc
HandlerFunc is a function type that handles HTTP requests in HX framework. It receives a context.Context and *http.Request as input parameters and returns an error. Context is identical to http.Request.Context, but it includes a ResponseWriter that can be hijacked.
If HandlerFunc returns: - nil: panics in dev mode, 500 in production — use hx.NoContent() for 204 responses - ProblemDetails: the response will be encoded as application/problem+json - Response: the response will be encoded as application/json with custom headers - any other error: the response will be 500 Internal Server Error
Example usage:
hx.HandlerFunc(func(ctx context.Context, r *http.Request) error {
// HandleFunc the request
return nil // or return an error
})
type HandlerFunc func(ctx context.Context, r *http.Request) errortype Middleware
Middleware wraps a HandlerFunc to form a processing chain. Return the next handler's error to propagate it or return a new error to short-circuit.
Example:
func authMiddleware(next hx.HandlerFunc) hx.HandlerFunc {
return func(ctx context.Context, r *http.Request) error {
if r.Header.Get("Authorization") == "" {
return hx.Unauthorized("missing authorization header")
}
return next(ctx, r)
}
}
type Middleware func(HandlerFunc) HandlerFuncfunc AdaptMiddleware
func AdaptMiddleware(m func(http.Handler) http.Handler) MiddlewareAdaptMiddleware converts standard net/http middleware into a hx Middleware.
It works correctly for middleware that:
- Enriches the request (adds context values, sets headers)
- Short-circuits by writing a response directly (e.g. rate limiters)
It does not work for middleware that transforms the ResponseWriter itself (e.g. gzip compression). Apply those at the server level instead.
type Mux
Mux is an interface that wraps the http.Handler.
type Mux interface {
http.Handler
Handle(pattern string, handler http.Handler)
}type Opt
type Opt interface {
// contains filtered or unexported methods
}func WithCustomMux
func WithCustomMux(mux Mux) OptWithCustomMux sets a custom multiplexer for the HX instance. The provided mux will be used for routing HTTP requests. If not set, http.DefaultServeMux will be used.
func WithLogger
func WithLogger(log *slog.Logger) OptWithLogger sets a custom logger for the HX instance. The provided logger will be used for error logging and debugging purposes. If not set, slog.Default() will be used.
func WithMiddlewares
func WithMiddlewares(m ...Middleware) OptWithMiddlewares sets middleware functions for the HX instance. These middlewares will be applied to all handlers in the order they are provided. Each middleware should implement the Middleware interface.
func WithProblemInstanceGetter(f func(ctx context.Context) string) OptWithProblemInstanceGetter sets a function that provides the "instance" value for ProblemDetails. This is particularly useful in distributed tracing scenarios or when using error tracking systems like Sentry, as it allows linking specific error instances to their corresponding traces or external error reports. The provided function receives a context and should return a string identifier that uniquely represents this error occurrence.
func WithProductionMode
func WithProductionMode(enabled bool) OptWithProductionMode enables or disables production mode.
type ProblemDetails
ProblemDetails is a JSON object that describes an error. https://datatracker.ietf.org/doc/html/rfc9457
type ProblemDetails = out.ProblemDetailsfunc BadRequest
func BadRequest(title string, opts ...ProblemOpt) ProblemDetailsBadRequest creates an HTTP response with a 400 (Bad Request) status code. It accepts a body of any type and optional response modifiers.
func Conflict
func Conflict(title string, opts ...ProblemOpt) ProblemDetailsConflict creates an HTTP response with a 409 (Conflict) status code. It accepts a body of any type and optional response modifiers.
func Forbidden
func Forbidden(title string, opts ...ProblemOpt) ProblemDetailsForbidden creates an HTTP response with a 403 (Forbidden) status code. It accepts a body of any type and optional response modifiers.
func Internal
func Internal(title string, opts ...ProblemOpt) ProblemDetailsInternal creates an HTTP response with a 500 (Internal Server Error) status code.
func MethodNotAllowed
func MethodNotAllowed(title string, opts ...ProblemOpt) ProblemDetailsMethodNotAllowed creates an HTTP response with a 405 (Method Not Allowed) status code. It accepts a body of any type and optional response modifiers.
func NotFound
func NotFound(title string, opts ...ProblemOpt) ProblemDetailsNotFound creates an HTTP response with a 404 (Not Found) status code. It accepts a body of any type and optional response modifiers.
func Problem
func Problem(status int, summary string, opts ...ProblemOpt) ProblemDetailsProblem creates a ProblemDetails instance with the provided status and summary.
func Unauthorized
func Unauthorized(title string, opts ...ProblemOpt) ProblemDetailsUnauthorized creates an HTTP response with a 401 (Unauthorized) status code. It accepts a body of any type and optional response modifiers.
type ProblemOpt
type ProblemOpt interface {
// contains filtered or unexported methods
}type Response
Response representing an HTTP response with status, body, and headers.
type Response = out.Responsefunc Created
func Created(body any, opts ...ResponseOpt) ResponseCreated creates an HTTP response with a 201 (Created) status code. It accepts a body of any type and optional response modifiers.
func NoContent
func NoContent() ResponseNoContent creates an HTTP response with a 204 (No Content) status code.
func OK
func OK(body any, opts ...ResponseOpt) ResponseOK creates a successful HTTP response with a 200 (OK) status code. It accepts a body of any type and optional response modifiers.
func Respond
func Respond(status int, body any, opts ...ResponseOpt) ResponseRespond creates an HTTP response with the specified status code and body. It accepts a status code, a body of any type, and optional response modifiers.
type ResponseOpt
ResponseOpt is an interface for options that can modify both Response and ProblemDetails objects. It provides methods to apply modifications to these types, allowing for flexible configuration of HTTP responses. Implementations of this interface can modify headers, cookies, and other response attributes consistently across both normal responses and problem details.
type ResponseOpt interface {
// contains filtered or unexported methods
}type RouteDoc
RouteDoc describes an HTTP operation for API documentation and spec generation.
type RouteDoc struct {
// Request is a zero-value of the request struct.
// Struct tags drive parameter and body extraction:
// json: request body field
// path: path parameter
// query: query parameter
// header: header parameter
// cookie: cookie parameter
// validate: constraints (required, min, max, enum, ...)
Request any
// Response is a zero-value of the primary success response body (200 OK).
Response any
// Responses document additional status codes and their response body types.
// Takes precedence over Response for the same status code.
// Use nil as the value to document a no-body response (e.g. 204).
// map[int]any{
// http.StatusCreated: OrderResponse{},
// http.StatusNoContent: nil,
// }
Responses map[int]any
// Errors list extra HTTP status codes this route returns as ProblemDetails.
// 400 and 500 are implicitly included on all routes.
// Only declare route-specific ones: 404, 409, 422, etc.
Errors []int
// Tags group this operation in the generated spec (e.g. "orders", "users").
Tags []string
// Summary is a short one-line description shown in tooling.
Summary string
// Description is a longer explanation. Markdown supported.
Description string
// OperationID is a unique identifier for code generators.
// Auto-derived from method + path if empty (e.g. "post_orders").
OperationID string
// Security references security scheme names defined at the server level.
// e.g. []string{"BearerAuth"}
Security []string
// Deprecated marks this operation as deprecated in the spec.
Deprecated bool
}type RouteInfo
RouteInfo is a registered route with its method, path, and optional documentation.
type RouteInfo struct {
Method string
Path string
Doc *RouteDoc
}type RouteOpt
RouteOpt is implemented by Middleware and Doc. Pass one or more RouteOpts to HandleFunc to configure a route.
type RouteOpt interface {
// contains filtered or unexported methods
}func Doc
func Doc(d RouteDoc) RouteOptDoc wraps a RouteDoc to be passed to HandleFunc alongside middlewares.
server.HandleFunc("POST /orders", HandleCreateOrder(svc),
hx.Doc(hx.RouteDoc{
Request: CreateOrderRequest{},
Response: OrderResponse{},
Summary: "Create a new order",
Tags: []string{"orders"},
}),
)
import "github.com/struct0x/hx/example"- func HandleBanUser() hx.HandlerFunc
- func HandleCancelOrder() hx.HandlerFunc
- func HandleCreateOrder() hx.HandlerFunc
- func HandleCreateUser() hx.HandlerFunc
- func HandleGetOrder() hx.HandlerFunc
- func HandleGetStats() hx.HandlerFunc
- func HandleGetUser() hx.HandlerFunc
- func HandleIngestEvent() hx.HandlerFunc
- func HandleListOrders() hx.HandlerFunc
- func HandleUpdateOrderStatus() hx.HandlerFunc
- type BanUserRequest
- type CancelOrderRequest
- type CreateOrderRequest
- type CreateOrderResponse
- type CreateUserRequest
- type GetOrderRequest
- type GetUserRequest
- type IngestEventRequest
- type ListOrdersRequest
- type Order
- type OrderList
- type StatsResponse
- type UpdateOrderStatusRequest
- type User
func HandleBanUser
func HandleBanUser() hx.HandlerFuncfunc HandleCancelOrder
func HandleCancelOrder() hx.HandlerFuncfunc HandleCreateOrder
func HandleCreateOrder() hx.HandlerFuncfunc HandleCreateUser
func HandleCreateUser() hx.HandlerFuncfunc HandleGetOrder
func HandleGetOrder() hx.HandlerFuncfunc HandleGetStats
func HandleGetStats() hx.HandlerFuncfunc HandleGetUser
func HandleGetUser() hx.HandlerFuncfunc HandleIngestEvent
func HandleIngestEvent() hx.HandlerFuncfunc HandleListOrders
func HandleListOrders() hx.HandlerFuncfunc HandleUpdateOrderStatus() hx.HandlerFunctype BanUserRequest
type BanUserRequest struct {
ID string `path:"id" validate:"required"`
Reason string `json:"reason" validate:"required,min=10,max=500"`
}type CancelOrderRequest
type CancelOrderRequest struct {
ID string `path:"id" validate:"required"`
Reason string `json:"reason" validate:"omitempty,max=500"`
}type CreateOrderRequest
type CreateOrderRequest struct {
UserID string `header:"X-User-ID" validate:"required"`
ProductName string `json:"product_name" validate:"required,min=2,max=100"`
Quantity int `json:"quantity" validate:"required,gte=1,lte=1000"`
Notes string `json:"notes" validate:"omitempty,max=500"`
}type CreateOrderResponse
type CreateOrderResponse struct {
Order *Order `json:"order"`
Message string `json:"message"`
}type CreateUserRequest
type CreateUserRequest struct {
Email string `json:"email" validate:"required,email"`
FirstName string `json:"first_name" validate:"required,alpha,min=2,max=50"`
LastName string `json:"last_name" validate:"required,alpha,min=2,max=50"`
Phone string `json:"phone" validate:"omitempty,e164"`
Country string `json:"country" validate:"omitempty,iso3166_1_alpha2"`
}type GetOrderRequest
type GetOrderRequest struct {
ID string `path:"id" validate:"required"`
}type GetUserRequest
type GetUserRequest struct {
ID string `path:"id" validate:"required"`
Verbose bool `query:"verbose"`
}type IngestEventRequest
type IngestEventRequest struct {
EventType string `json:"event_type" validate:"required,oneof=order.created order.cancelled user.registered"`
Payload map[string]any `json:"payload" validate:"required"`
}type ListOrdersRequest
type ListOrdersRequest struct {
UserID string `header:"X-User-ID" validate:"required"`
Status string `query:"status" validate:"omitempty,oneof=pending processing shipped delivered cancelled"`
SortBy string `query:"sort_by" validate:"omitempty,oneof=created_at total_price"`
Page int `query:"page" validate:"omitempty,gte=1"`
PerPage int `query:"per_page" validate:"omitempty,gte=1,lte=100"`
}type Order
type Order struct {
ID string `json:"id"`
UserID string `json:"user_id"`
ProductName string `json:"product_name"`
Quantity int `json:"quantity"`
TotalPrice float64 `json:"total_price"`
Status string `json:"status"`
Notes string `json:"notes,omitempty"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}type OrderList
type OrderList struct {
Orders []Order `json:"orders"`
Total int `json:"total"`
Page int `json:"page"`
PerPage int `json:"per_page"`
}type StatsResponse
type StatsResponse struct {
TotalOrders int `json:"total_orders"`
TotalRevenue float64 `json:"total_revenue"`
PendingOrders int `json:"pending_orders"`
TotalUsers int `json:"total_users"`
}type UpdateOrderStatusRequest struct {
ID string `path:"id" validate:"required"`
Status string `json:"status" validate:"required,oneof=processing shipped delivered cancelled"`
}type User
type User struct {
ID string `json:"id"`
Email string `json:"email"`
FirstName string `json:"first_name"`
LastName string `json:"last_name"`
Phone string `json:"phone,omitempty"`
Country string `json:"country,omitempty"`
Banned bool `json:"banned"`
CreatedAt time.Time `json:"created_at"`
}import "github.com/struct0x/hx/hxdoc"Package hxdoc generates an OpenAPI 3.1.0 specification from routes registered with hx.HX and exposes it as a hx.HandlerFunc.
Usage:
hx.HandleFunc("/openapi.json", hxdoc.Handler(server,
hxdoc.WithTitle("Orders API"),
hxdoc.WithVersion("1.0.0"),
hxdoc.WithSecurityScheme("BearerAuth", hxdoc.BearerAuth()),
))
- func Handler(server *hx.HX, opts ...Opt) hx.HandlerFunc
- func UIHandler(specURL string) hx.HandlerFunc
- type Opt
- type Schema
- type SecurityScheme
func Handler
func Handler(server *hx.HX, opts ...Opt) hx.HandlerFuncHandler returns an hx.HandlerFunc that serves the OpenAPI 3.1.0 spec as JSON. The spec is generated once at construction time from the routes registered on server. Only routes with an hx.Doc(...) option are included in the spec.
func UIHandler
func UIHandler(specURL string) hx.HandlerFuncUIHandler returns a hx.HandlerFunc that serves a Swagger UI HTML page loading the spec from specURL.
Register it on a hx server:
server.HandleFunc("GET /docs", hxdoc.UIHandler("/openapi.json"))
type Opt
Opt configures the generated OpenAPI document.
type Opt func(*config)func WithDescription
func WithDescription(desc string) OptWithDescription sets the API description in the info object.
func WithSecurityScheme
func WithSecurityScheme(name string, scheme SecurityScheme) OptWithSecurityScheme adds a named security scheme to the components section.
func WithServer
func WithServer(url, description string) OptWithServer adds a server entry to the OpenAPI document.
func WithTitle
func WithTitle(title string) OptWithTitle sets the API title in the info object.
func WithVersion
func WithVersion(version string) OptWithVersion sets the API version in the info object.
type Schema
Schema is a JSON Schema definition for OpenAPI 3.1.0.
type Schema struct {
Ref string `json:"$ref,omitempty"`
Type string `json:"type,omitempty"`
Format string `json:"format,omitempty"`
Pattern string `json:"pattern,omitempty"`
Properties map[string]*Schema `json:"properties,omitempty"`
Required []string `json:"required,omitempty"`
Items *Schema `json:"items,omitempty"`
AdditionalProperties *Schema `json:"additionalProperties,omitempty"`
Enum []any `json:"enum,omitempty"`
Minimum *float64 `json:"minimum,omitempty"`
Maximum *float64 `json:"maximum,omitempty"`
ExclusiveMinimum *float64 `json:"exclusiveMinimum,omitempty"`
ExclusiveMaximum *float64 `json:"exclusiveMaximum,omitempty"`
MinLength *int `json:"minLength,omitempty"`
MaxLength *int `json:"maxLength,omitempty"`
MinItems *int `json:"minItems,omitempty"`
MaxItems *int `json:"maxItems,omitempty"`
UniqueItems bool `json:"uniqueItems,omitempty"`
}type SecurityScheme
SecurityScheme defines an OpenAPI security scheme.
type SecurityScheme struct {
Type string `json:"type"`
Scheme string `json:"scheme,omitempty"`
BearerFormat string `json:"bearerFormat,omitempty"`
In string `json:"in,omitempty"`
Name string `json:"name,omitempty"`
Description string `json:"description,omitempty"`
}func APIKeyAuth
func APIKeyAuth(in, name string) SecuritySchemeAPIKeyAuth returns an API key security scheme.
func BearerAuth
func BearerAuth() SecuritySchemeBearerAuth returns a Bearer token security scheme.
import "github.com/struct0x/hx/hxmid"Package hxmid provides standard middleware for hx applications.
- func Logger(log *slog.Logger, extras ...AttrsFunc) hx.Middleware
- func Recoverer(log *slog.Logger) hx.Middleware
- func RequireJSON(opts ...hx.ProblemOpt) hx.Middleware
- type AttrsFunc
func Logger
func Logger(log *slog.Logger, extras ...AttrsFunc) hx.MiddlewareLogger logs each request with its method, path, status code, and duration. Requests resulting in 5xx are logged at Error level, 4xx at Warn, everything else at Info.
Additional attributes can be injected per-request via optional AttrsFuncs:
hxmid.Logger(log, func(ctx context.Context, r *http.Request) []slog.Attr {
return []slog.Attr{
slog.String("trace_id", trace.SpanFromContext(ctx).SpanContext().TraceID().String()),
}
})
func Recoverer
func Recoverer(log *slog.Logger) hx.MiddlewareRecoverer catches panics in downstream handlers, logs them with a stack trace, and returns a 500 Internal Server Error response.
func RequireJSON
func RequireJSON(opts ...hx.ProblemOpt) hx.MiddlewareRequireJSON rejects requests with a body (POST, PUT, PATCH) that do not declare Content-Type: application/json, responding with 415 Unsupported Media Type. Optional ProblemOpts are forwarded to the error response, e.g. hx.WithTypeURI for docs links.
type AttrsFunc
AttrsFunc extracts additional slog attributes from the request context. Use this to attach trace IDs, user IDs, or any other contextual fields to log entries.
type AttrsFunc func(ctx context.Context, r *http.Request) []slog.Attrimport "github.com/struct0x/hx/hxtest"Package hxtest provides a fluent API for testing hx.HandlerFunc.
- type Check
- type Result
- type Tester
- func Test(t testing.TB, h hx.HandlerFunc) *Tester
- func (tt *Tester) DebugBody(b bool) *Tester
- func (tt *Tester) Do(req *http.Request) *Result
- func (tt *Tester) Expect(e Check) *Tester
- func (tt *Tester) Expects(ee ...Check) *Tester
- func (tt *Tester) WithContext(mut func(ctx context.Context) context.Context) *Tester
type Check
Check is a test check applied to a Result.
type Check func(t testing.TB, r *Result)func Body
func Body(want any, opts ...cmp.Option) Checkfunc Header
func Header(key, want string) Checkfunc HeaderHas
func HeaderHas(key, substr string) Checkfunc IsProblem
func IsProblem(problem error) CheckIsProblem asserts RFC 9457 style response (& Content-Type).
func NoBody
func NoBody() Checkfunc ProblemDetail
func ProblemDetail(substr string) Checkfunc ProblemTitle
func ProblemTitle(substr string) Checkfunc Status
func Status(code int) Checktype Result
Result is a materialized HTTP response produced by running a handler.
type Result struct {
ContentType string
Status int
Header http.Header
Body any
Hijacked bool
Err error
Problem *out.ProblemDetails
}type Tester
Tester is the harness builder. Create via Test(t, handler).
type Tester struct {
// contains filtered or unexported fields
}func Test
func Test(t testing.TB, h hx.HandlerFunc) *TesterTest initializes a test harness for a single handler.
func (*Tester) DebugBody
func (tt *Tester) DebugBody(b bool) *TesterDebugBody enables debug logging of the response body.
func (*Tester) Do
func (tt *Tester) Do(req *http.Request) *ResultDo run the handler against req, honoring HijackResponseWriter semantics, materializes the result, then runs queued expectations (fail-fast).
func (*Tester) Expect
func (tt *Tester) Expect(e Check) *TesterExpect queues an assertion to run after the handler is executed.
func (*Tester) Expects
func (tt *Tester) Expects(ee ...Check) *TesterExpects queues multiple assertions to run after the handler is executed.
func (*Tester) WithContext
func (tt *Tester) WithContext(mut func(ctx context.Context) context.Context) *TesterWithContext lets you mutate the request context before invocation.
import "github.com/struct0x/hx/internal"- func HijackResponseWriter(ctx context.Context) http.ResponseWriter
- func PeekResponseWriter(ctx context.Context) (http.ResponseWriter, *atomic.Bool)
- func WithResponseWriter(ctx context.Context, rwRead *atomic.Bool, rw http.ResponseWriter) context.Context
func HijackResponseWriter
func HijackResponseWriter(ctx context.Context) http.ResponseWriterHijackResponseWriter retrieves the http.ResponseWriter from the context and marks it as taken. When hijacked, the return value from HandlerFunc will be ignored.
func PeekResponseWriter
func PeekResponseWriter(ctx context.Context) (http.ResponseWriter, *atomic.Bool)PeekResponseWriter retrieves the http.ResponseWriter and its write flag from context without marking it as hijacked.
func WithResponseWriter
func WithResponseWriter(ctx context.Context, rwRead *atomic.Bool, rw http.ResponseWriter) context.ContextWithResponseWriter stores the http.ResponseWriter in the context. It returns a new context containing the response writer.
import "github.com/struct0x/hx/internal/bind"var (
ErrNilRequest = errors.New("bind: request is nil")
ErrNilDestination = errors.New("bind: destination is nil")
ErrNotAStruct = errors.New("bind: destination must be a struct")
ErrExpectedStruct = errors.New("bind: expected struct")
ErrMultipleTags = errors.New("bind: multiple tags")
ErrEmptyTag = errors.New("bind: tag is empty")
)func Bind
func Bind(r *http.Request, dst any, opts ...Opt) errortype Error
Error represents a failure to bind a single struct field.
type Error struct {
Field string // struct field name
Err error // underlying error (conversion, missing tag, etc.)
}func (Error) Error
func (e Error) Error() stringError implements the error interface.
type Errors
Errors aggregate one or more BindError values. It implements the error interface.
type Errors struct {
Errors []Error
}func (*Errors) Append
func (es *Errors) Append(err Error)func (*Errors) Error
func (es *Errors) Error() stringError returns a human‑readable summary.
func (*Errors) Unwrap
func (es *Errors) Unwrap() []errorUnwrap enables errors.Is / errors.As to work on individual errors.
type Opt
Opt is a functional option that configures the binder.
type Opt interface {
BindOpt()
}func WithMaxFormMemoryMB
func WithMaxFormMemoryMB(maxFormMemoryMB int64) Optfunc WithPathValueFunc
func WithPathValueFunc(fn func(r *http.Request, name string) string) Optfunc WithValidator
func WithValidator(v *validator.Validate) Optimport "github.com/struct0x/hx/internal/out"type ProblemDetails
ProblemDetails is a JSON object that describes an error. https://datatracker.ietf.org/doc/html/rfc9457
type ProblemDetails struct {
// Type is a URI reference [RFC3986] that identifies the problem type.
Type string
// StatusCode is the HTTP status code generated by the origin server for this occurrence of the problem.
StatusCode int
// Title contains a short, human-readable summary of the problem.
Title string
// Detail contains a human-readable explanation specific to this occurrence of the problem.
Detail string
// Instance is a URI reference that identifies the specific occurrence of the problem.
Instance string
// Extensions is a map of additional information about the problem. They will be inlined in the JSON response.
Extensions map[string]any
// Cause is the underlying error that caused this problem. This will be logged.
Cause error
Headers http.Header
Cookies []*http.Cookie
}func (ProblemDetails) Error
func (p ProblemDetails) Error() stringfunc (ProblemDetails) MarshalJSON
func (p ProblemDetails) MarshalJSON() ([]byte, error)func (*ProblemDetails) UnmarshalJSON
func (p *ProblemDetails) UnmarshalJSON(bytes []byte) errorfunc (ProblemDetails) Unwrap
func (p ProblemDetails) Unwrap() errortype Response
type Response struct {
ContentType string
StatusCode int
Body any
Headers http.Header
Cookies []*http.Cookie
}func (Response) Error
func (r Response) Error() stringGenerated by gomarkdoc