Documentation
¶
Index ¶
- func RefuseOnErr(h http.Handler, a ErrorChecker) http.Handler
- func RefuseOnErrWith(h http.Handler, a ErrorChecker, errFn ErrorHandler) http.Handler
- func SplitMW(mws []MW, errs ErrorSink) ([]Middleware, []MW)
- func Wrap(chain []Middleware, h http.Handler) http.Handler
- type Adapter
- type DocCarrier
- type EngineProvider
- type ErrorChecker
- type ErrorHandler
- type ErrorSink
- type ListError
- type MW
- type Middleware
- type RegistryProvider
- type RegistrySnapshot
- type RouteDocMeta
- type RouteRecord
- type Router
- func (r *Router) Engine() any
- func (r *Router) Err() error
- func (r *Router) Group(prefix string, mws ...MW) Adapter
- func (r *Router) Handle(method, p string, h http.Handler, mws ...MW)
- func (r *Router) HandleFunc(method, p string, h http.HandlerFunc, mws ...MW)
- func (r *Router) HandleInternal(method, p string, h http.Handler, mws ...MW)
- func (r *Router) HandleInternalFunc(method, p string, h http.HandlerFunc, mws ...MW)
- func (r *Router) Registry() RegistrySnapshot
- func (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request)
- func (r *Router) Use(mws ...MW)
- func (r *Router) With(mws ...MW) Adapter
- type ScopeID
- type ScopeKind
- type ScopeRecord
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func RefuseOnErr ¶
func RefuseOnErr(h http.Handler, a ErrorChecker) http.Handler
RefuseOnErr wraps h so that every incoming request first checks a.Err().
If a.Err() returns a non-nil error, the request is rejected with "503 Service Unavailable" and a generic plain-text body; h is never called. If a.Err() returns nil, the request is forwarded to h without modification.
RefuseOnErr is safe to call with a nil h or a nil a:
- nil a: the wrapper delegates to h (no check is possible).
- nil h: every request is rejected (via errNoHandler).
func RefuseOnErrWith ¶
func RefuseOnErrWith(h http.Handler, a ErrorChecker, errFn ErrorHandler) http.Handler
RefuseOnErrWith is like RefuseOnErr but accepts a custom ErrorHandler.
If errFn is nil, the default generic 503 handler is used (identical to RefuseOnErr behavior).
func SplitMW ¶
func SplitMW(mws []MW, errs ErrorSink) ([]Middleware, []MW)
SplitMW separates portable net/http middleware from native MW.
- Portable middleware is returned as a slice of Middleware (to be wrapped around handlers). - Native middleware is returned as MWs to be applied via MW.Apply(driver).
Any nil MW is recorded as an error (if errs != nil).
Types ¶
type Adapter ¶
type Adapter interface {
http.Handler
// Use registers middleware on the current router scope.
//
// The middleware is applied to all routes registered on this router and on any sub-routers created
// from it via Group or With (unless a derived router is intentionally isolated by the adapter).
//
// In v1, only portable net/http middleware is accepted. Any non-portable middleware is rejected and
// recorded as an error retrievable via Err() (typically [jetwarp.ErrNativeMWUnsupported]).
//
// Use does not panic; invalid inputs are reported via Err().
Use(mws ...MW)
// Group creates a new router that prefixes all routes with prefix and applies the given middleware
// to routes registered on the group.
//
// Group implements logical scoping: it prefixes all routes with prefix using path concatenation
// managed by the adapter. Router-native scoping (chi.Mount, gin/echo/fiber groups, etc.) is not
// activated by the adapter in v1. For router-native scoping when using drivers directly, see
// drv.Drv.Scope().
//
// Prefix rules (v1):
// - Group("") and Group("/") are no-ops: they do not add a prefix and do not record an error.
// - Whitespace-only prefixes (e.g. " ") are invalid and record ErrInvalidGroupPrefix.
// - Other prefixes are normalized and validated as route patterns. If invalid, the adapter records
// ErrInvalidGroupPrefix and the group behaves as a no-op prefix.
//
// Middleware order (v1):
// Use → Group → With → per-route → handler
//
// In v1, only portable net/http middleware is supported. Non-portable middleware results in
// ErrNativeMWUnsupported.
Group(prefix string, mws ...MW) Adapter
// With returns a derived router scope that adds middleware to subsequently registered routes without
// mutating the receiver.
//
// With is useful for temporary scoping:
// r.With(Auth()).HandleFunc("GET", "/private", h)
//
// Middleware ordering still follows:
// Global (Use) → Group (if any) → With → Per-route → Handler.
//
// With does not panic; unsupported middleware is reported via Err() on the returned Router.
With(mws ...MW) Adapter
// Handle registers an [http.Handler] for the given HTTP method and path pattern.
//
// method is typically an uppercase token such as "GET", "POST", "PUT", "DELETE", "OPTIONS".
// Adapters may normalize method casing.
//
// path uses the jetwarp canonical pattern syntax (e.g. "/domain/subdomain/{id}.json").
//
// mws are per-route middleware applied after any global/group/With middleware.
//
// Handle does not panic; invalid patterns or unsupported adapter features are reported via Err().
Handle(method, path string, h http.Handler, mws ...MW)
// HandleFunc is like Handle but accepts an http.HandlerFunc.
HandleFunc(method, path string, h http.HandlerFunc, mws ...MW)
// Err returns accumulated configuration errors encountered during registration.
//
// Err MUST be checked before serving traffic. A non-nil error indicates that at least one route
// or middleware could not be applied as requested, and runtime behavior may be incomplete.
//
// Implementations typically return a multi-error type; callers should treat it as opaque and log it.
Err() error
}
Adapter is a framework-agnostic HTTP router abstraction.
Adapter provides a small, stable API for registering routes and middleware while allowing applications to "bring their own router" from the Go ecosystem (stdlib mux, chi, gin, echo, fiber, etc.) through adapter packages.
jetwarp uses a canonical path-pattern syntax based on Go 1.22+ ServeMux routing:
- Path parameters use braces: /users/{id}
- Literal suffixes are supported: /items/{id}.json, /items.json
- Query strings are not part of route matching.
Middleware ¶
In v1, Adapter supports portable net/http middleware only. Wrap middleware values (func(http.Handler) http.Handler) using HTTP or HTTPNamed.
Any middleware spec that is not portable net/http middleware is rejected in v1 and recorded as jetwarp.ErrNativeMWUnsupported (retrievable via Err()).
Middleware application order is deterministic:
Global (Use) → Group → With → Per-route → Handler.
Errors and validation ¶
Some operations can fail at registration time (invalid patterns, unsupported middleware, etc.). Adapter implementations MUST NOT panic on invalid inputs. Instead, they accumulate configuration errors which are returned by Err().
If Err() is non-nil, the Adapter should be considered unusable for serving production traffic. Server wrappers are expected to refuse to start when Err() != nil. For a runtime guard, use RefuseOnErr or RefuseOnErrWith from this package. For a belt-and-suspenders runtime guard (useful in staging/tests), you can wrap the live handler with RefuseOnErr so requests are rejected when Err() becomes non-nil:
if err := r.Err(); err != nil {
log.Fatalf("router setup failed: %v", err)
}
http.ListenAndServe(":8080", RefuseOnErr(r, r))
Concurrency ¶
Route registration is expected to happen during initialization before the Adapter is used to serve requests. Unless explicitly documented by a specific adapter, Adapter methods are not guaranteed to be safe for concurrent use with ServeHTTP.
type DocCarrier ¶
type DocCarrier interface {
MW
areg.DocCarrier
}
DocCarrier is implemented by MWs that carry documentation metadata.
Core recognises DocCarrier MWs and strips them from the middleware chain before SplitMW runs, so they never reach the driver or the handler wrapper. This keeps [Err] clean: a DocCarrier does not trigger [ErrNativeMWUnsupported].
Concrete DocCarrier implementations live in openapi/oas3 or a dedicated doc package so the adapter package itself has no dependency on any OpenAPI types.
Note: adapter/registry defines a lighter DocCarrier (without MW) so tooling can depend on it without importing the routing API.
type EngineProvider ¶
type EngineProvider interface {
Engine() any
}
EngineProvider is an optional escape hatch for retrieving the underlying framework engine/router.
The concrete type returned by Engine() depends on the adapter (e.g., *gin.Engine, *echo.Echo, *fiber.App, chi.Router, *http.ServeMux).
WARNING: Using EngineProvider couples your application to a specific router implementation and may reduce portability. Prefer the Router API when possible.
type ErrorChecker ¶
type ErrorChecker interface {
Err() error
}
ErrorChecker is the minimal interface required by the safe-serve helpers.
*Router (and therefore any Adapter) satisfies this interface automatically. Third-party types that expose an Err() error method also satisfy it, making the helpers router-agnostic.
type ErrorHandler ¶
type ErrorHandler func(err error, w http.ResponseWriter, r *http.Request)
ErrorHandler is a user-supplied callback invoked by RefuseOnErrWith when the ErrorChecker reports a non-nil error, or when no underlying http.Handler is provided.
Implementations MUST write a response (status + body). The error passed to the handler is guaranteed to be non-nil.
type MW ¶
type MW interface {
// Apply attempts to attach this middleware spec to the given driver scope.
//
// For portable middleware, Apply is typically a no-op (portable middleware is applied by core).
// In v1, any non-portable middleware should fail with [jetwarp.ErrNativeMWUnsupported].
Apply(d drv.Drv) error
// String returns a human-readable description for diagnostics.
String() string
}
MW is a middleware specification accepted by Adapter.Use/Group/With/Handle.
In v1, MW represents portable net/http middleware only, created via HTTP or HTTPNamed.
The interface includes Apply(d drv.Drv) for forward compatibility. In v1, portable middleware is applied by core via handler wrapping (Apply is a no-op for portable middleware), and any non-portable middleware SHOULD return jetwarp.ErrNativeMWUnsupported.
Portable middleware application order is deterministic:
Global (Use) → Group → With → Per-route → Handler.
MW.Apply MUST NOT panic; it must return an error on incompatibility.
func HTTPNamed ¶
func HTTPNamed(name string, mw Middleware) MW
HTTPNamed is like HTTP but allows setting a diagnostic name (recommended).
type Middleware ¶
Middleware is portable net/http middleware.
It follows the standard convention:
mw := func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// before
next.ServeHTTP(w, r)
// after
})
}
type RegistryProvider ¶
type RegistryProvider interface {
Registry() RegistrySnapshot
}
RegistryProvider is an optional interface implemented by adapters that can expose a read-only view of the registered routes and scopes.
It is intentionally separate from Adapter to preserve the stability of the core API. Consumers (e.g., OpenAPI generators) should use a type assertion:
if rp, ok := a.(adapter.RegistryProvider); ok { snap := rp.Registry() }
type RegistrySnapshot ¶
type RouteDocMeta ¶
type RouteDocMeta = areg.RouteDocMeta
RouteDocMeta holds OpenAPI documentation metadata associated with a route or scope.
Values are collected from DocCarrier MWs passed to Use/Group/With/Handle and merged through the scope hierarchy using the rules in MergeDocMeta.
The zero value is valid and means "no annotation".
This type is defined in the adapter/registry subpackage and re-exported here to keep adapter's public surface stable.
func MergeDocMeta ¶
func MergeDocMeta(a, b RouteDocMeta) RouteDocMeta
MergeDocMeta merges b into a and returns the result.
Merge rules (per spec):
- OperationID: last non-empty wins (most-specific scope/route overrides)
- Summary: last non-empty wins
- Tags: append + deduplicate, first-seen order preserved across scopes
- Deprecated: true if either a or b is true (monotonic — never goes back to false)
type RouteRecord ¶
type RouteRecord = areg.RouteRecord
type Router ¶
type Router struct {
// contains filtered or unexported fields
}
Router is the core Adapter implementation over a driver.Drv.
Users consume it through concrete adapter packages (adapter/stdlib, adapter/chi, ...).
func (*Router) Group ¶
Group creates a derived router with a prefixed path scope and optional middleware.
func (*Router) HandleFunc ¶
func (r *Router) HandleFunc(method, p string, h http.HandlerFunc, mws ...MW)
func (*Router) HandleInternal ¶ added in v1.0.0
HandleInternal registers a route like [Handle], but marks the registration as internal.
Internal routes are intended for infrastructure endpoints mounted by jetwarp submodules (e.g. OpenAPI JSON and docs UIs). They remain visible in the registry snapshot for debugging/tooling, but generators like openapi/oas3 should exclude them from output.
This method is an optional extension hook; it is not part of the Adapter interface.
func (*Router) HandleInternalFunc ¶ added in v1.0.0
func (r *Router) HandleInternalFunc(method, p string, h http.HandlerFunc, mws ...MW)
func (*Router) Registry ¶
func (r *Router) Registry() RegistrySnapshot
Registry returns a read-only snapshot of all scopes and attempted route registrations.
The returned snapshot is safe to use without holding any locks.
func (*Router) Use ¶
Use registers middleware on the current router scope.
Portable middleware is stored and applied by wrapping handlers during Handle(). DocCarrier MWs are extracted and stored as local doc metadata for this scope. Non-portable (native) MWs are recorded as errors (not supported in this core adapter).
type ScopeRecord ¶
type ScopeRecord = areg.ScopeRecord