-
Notifications
You must be signed in to change notification settings - Fork 0
Extensions
An extension is a module implementing five lifecycle hooks. It depends on
rextension — a contract module
with no dependencies — not on rex, so adding one never pulls the framework in
twice.
app := rex.New(
rex.WithConfig(cfg),
cors.WithCORS(corsCfg),
security.WithSecurity(secCfg),
ratelimit.WithRateLimit(ratelimit.WithGlobalLimit(100, time.Minute)),
validation.WithValidation(nil),
health.WithHealth(nil),
metric.WithMetrics(nil),
openapi.WithOpenAPI(apiCfg),
swagger.WithSwagger(nil),
)Argument order does not matter. Middleware order comes from the priority scale.
| Module | With… |
Attaches | Wiki |
|---|---|---|---|
rextension-cors |
cors.WithCORS(cfg) |
per-router middleware at PriorityCORS; decorates the router's own OPTIONS answer |
wiki |
rextension-ratelimit |
ratelimit.WithRateLimit(opts…) |
per-route middleware at PriorityRateLimit; X-RateLimit-* headers |
wiki |
rextension-security |
security.WithSecurity(cfg) |
auth at PriorityAuth, CSRF at PriorityCSRF; publishes the scheme registry; validates the route table |
wiki |
rextension-validation |
validation.WithValidation(cfg) |
per-route middleware at PriorityValidation, driven by route body schemas |
wiki |
rextension-health |
health.WithHealth(cfg) |
/healthz, /readyz on a dedicated router; dependency gate at PriorityHealthGate
|
wiki |
rextension-metric |
metric.WithMetrics(cfg) |
/metrics on a dedicated router; instrumentation at PriorityDefault
|
wiki |
rextension-openapi |
openapi.WithOpenAPI(cfg) |
/openapi.json, generated from the route table in OnStart
|
wiki |
rextension-swagger |
swagger.WithSwagger(cfg) |
Swagger UI, served from embedded assets | wiki |
Each also has a New…Extension(cfg) constructor, if you want the extension
value rather than an Option. A nil config takes safe defaults everywhere.
Through route interfaces. A route declares a capability; whichever extension cares type-asserts for it. A route that does not implement one is passed through.
type CreateUser struct{ route.Route }
func (r *CreateUser) RequiredSchemes() []string { return []string{"bearer"} } // security, openapi, swagger
func (r *CreateUser) RequestBody() rextension.BodySchema { return rextension.Scalar(Req{}) } // validation, openapi
func (r *CreateUser) MaxBodyBytes() int64 { return 1 << 20 } // the routerThrough the container. The security extension publishes a
rextension.SchemeRegistry; OpenAPI and Swagger resolve it, and degrade quietly
when it is absent.
Through shared contract types. rextension.OriginPolicy is written once and
read by both CORS and CSRF — one allowlist, two consumers.
Never through package-level state. A global registry means two Rex instances in one process clobber each other and state leaks between tests in the same binary.
They may create routers, register routes, attach middleware, register services in the container, subscribe to events, and validate the complete route table before it freezes.
They may not register routes or middleware from OnReady — the table is
frozen by then, and the attempt is refused with ErrRouterFrozen. If you are
upgrading and something stopped appearing, this is almost certainly why.
func main() {
cfg := &rex.Config{
DefaultRouter: rex.RouterConfig{Addr: ":8080", BaseURL: "/api/v1"},
ShutdownTimeout: 30 * time.Second,
}
origins := rextension.OriginPolicy{
AllowedOrigins: []string{"https://app.example.com"},
AllowCredentials: true,
}
app := rex.New(
rex.WithConfig(cfg),
cors.WithCORS(cors.NewConfig(cors.WithPolicy(origins))),
security.WithSecurity(securityConfig(origins)),
ratelimit.WithRateLimit(ratelimit.WithGlobalLimit(600, time.Minute)),
validation.WithValidation(nil),
health.WithHealth(nil), // :9090
metric.WithMetrics(nil), // :9091
openapi.WithOpenAPI(nil),
swagger.WithSwagger(nil),
)
if err := registerRoutes(app); err != nil {
log.Fatal(err)
}
if err := app.Run(); err != nil {
log.Fatal(err)
}
}The resulting chain on an application route, outermost first:
CORS (200) → rate limit (300) → auth (400) → CSRF (450)
→ validation (500) → health gate (600) → metrics (1000) → handler
See rextension → Writing an extension. The short version:
- import
rextensiononly — notrex, notdix - implement all five hooks, even as no-ops
- declare in
OnInitialize/OnStart, never inOnReady - tolerate
ErrRouterExistsfromCreateRouter - read route configuration once, at build time, in a
PerRouteMiddlewarefactory - answer with problem documents
rex — Restful Extended eXperience · MIT · © 2026 Kryovyx · pre-1.0 (alpha), interfaces may change
Getting started
Core
Operating
Ecosystem