adapter

package
v1.0.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Feb 25, 2026 License: Apache-2.0 Imports: 10 Imported by: 0

Documentation

Index

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).

func Wrap

func Wrap(chain []Middleware, h http.Handler) http.Handler

Wrap applies middleware in order around h (first is outermost).

Given chain [A,B,C] and handler H => A(B(C(H))).

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 ErrorSink

type ErrorSink interface {
	Add(error)
}

type ListError

type ListError struct {
	// contains filtered or unexported fields
}

func (*ListError) Add

func (e *ListError) Add(err error)

func (*ListError) Error

func (e *ListError) Error() string

func (*ListError) Unwrap

func (e *ListError) Unwrap() []error

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 HTTP

func HTTP(mw Middleware) MW

HTTP wraps a portable net/http middleware as an MW.

func HTTPNamed

func HTTPNamed(name string, mw Middleware) MW

HTTPNamed is like HTTP but allows setting a diagnostic name (recommended).

type Middleware

type Middleware func(http.Handler) http.Handler

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 RegistrySnapshot = areg.Snapshot

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 New

func New(d drv.Drv) *Router

New constructs a new core router adapter over a driver.

func (*Router) Engine

func (r *Router) Engine() any

func (*Router) Err

func (r *Router) Err() error

func (*Router) Group

func (r *Router) Group(prefix string, mws ...MW) Adapter

Group creates a derived router with a prefixed path scope and optional middleware.

func (*Router) Handle

func (r *Router) Handle(method, p string, h http.Handler, mws ...MW)

func (*Router) HandleFunc

func (r *Router) HandleFunc(method, p string, h http.HandlerFunc, mws ...MW)

func (*Router) HandleInternal added in v1.0.0

func (r *Router) HandleInternal(method, p string, h http.Handler, mws ...MW)

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) ServeHTTP

func (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request)

func (*Router) Use

func (r *Router) Use(mws ...MW)

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).

func (*Router) With

func (r *Router) With(mws ...MW) Adapter

With returns a derived router scope that adds middleware without mutating the receiver.

type ScopeID

type ScopeID = areg.ScopeID
const NoParentScopeID ScopeID = areg.NoParentScopeID

type ScopeKind

type ScopeKind = areg.ScopeKind
const (
	ScopeRoot  ScopeKind = areg.ScopeRoot
	ScopeGroup ScopeKind = areg.ScopeGroup
	ScopeWith  ScopeKind = areg.ScopeWith
)

type ScopeRecord

type ScopeRecord = areg.ScopeRecord

Directories

Path Synopsis
chi module
echo module
fiber module
gin module
Package registry provides read-only observation types produced by adapters.
Package registry provides read-only observation types produced by adapters.
stdlib module

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL