Skip to content
wiki edited this page Sep 4, 2026 · 1 revision

rex — Restful Extended eXperience

An extension-based HTTP framework for Go. A trie router, a lifecycle, a DI container, an event bus, and a set of extensions that plug into all of it without depending on any of it.

go get github.com/kryovyx/rex

Requires Go 1.27+.

Hello, world

package main

import (
	"github.com/kryovyx/rex"
	"github.com/kryovyx/rex/route"
)

func main() {
	app := rex.New()

	if err := app.RegisterRoute(route.New("GET", "/hello", func(ctx route.Context) {
		_ = ctx.Text(200, "Hello, World!")
	})); err != nil {
		panic(err)
	}

	if err := app.Run(); err != nil { // blocks until SIGINT/SIGTERM
		panic(err)
	}
}

The one thing to understand first

New() collects. Run() builds.

New creates the container, the event bus and the logger, applies your options, and returns. It creates no routers, runs no extension hooks, binds nothing, and cannot fail.

Run freezes the configuration, creates the routers, runs the extension hooks, composes every middleware chain, builds and freezes every route table, validates, binds the listeners synchronously, and only then reports readiness.

Two consequences you will feel immediately:

  • Declaration order does not matter. Middleware before its router, a route before the router it names, configuration after everything — all fine, because nothing is resolved until Run.
  • Nothing may be registered after Run. The route table is published by atomic swap and read without a lock, so there is nowhere safe to add to it. A late registration is refused with ErrRouterFrozen, not silently dropped.

See Lifecycle.

What is in the box

Routing trie router with {param} and * segments, real 404/405 distinction, router-answered OPTIONS
Routers and Listeners any number of named listeners — public API on :8080, admin on :9090, metrics on :9091
Middleware a fixed priority scale, per-route and per-router factories, chains composed once at build
Dependency Injection dix container, with a scope per request
Events typed router events on a non-blocking bus
Logging log/slog-backed, structured, swappable
Errors on the Wire RFC 9457 problem documents, from the framework and every extension
TLS and Listener Limits per-handshake certificates, mTLS, timeouts and body caps that are actually set
Graceful Shutdown signal-driven, bounded, runs exactly once

The extension ecosystem

Extensions depend on rextension — a contract module with no dependencies — not on rex. Adding one to your application never adds the framework twice.

Module Does
rextension the contract extension authors build against
rextension-cors Cross-Origin Resource Sharing
rextension-health liveness, readiness, dependency gating
rextension-metric Prometheus / OpenMetrics
rextension-openapi OpenAPI 3.1 generation
rextension-ratelimit global, per-router and per-endpoint limits
rextension-security authentication, roles, scopes, CSRF
rextension-swagger Swagger UI
rextension-validation request and response validation
app := rex.New(
	cors.WithCORS(corsCfg),
	security.WithSecurity(secCfg),
	health.WithHealth(nil),
	metric.WithMetrics(nil),
	openapi.WithOpenAPI(apiCfg),
	swagger.WithSwagger(nil),
)

See Extensions for what each one attaches, and in what order.

Status

Pre-1.0 (alpha). Interfaces may change between minor versions, and the project is not open to external contributions yet.

If you are upgrading from v0.2.1, read the workspace's MIGRATION-v0.3.0.md first — v0.3.0 is one architectural change (the frozen route table) with many consequences, and it fixes four extensions that were silently doing nothing.

Clone this wiki locally