lightmux/README.md
juancwu 22277186ae extract middlewares to lightmux-contrib
Removes pkg/middleware. The Logger, Recoverer, and RealIP middlewares
now live in the sibling lightmux-contrib module as the realip,
requestlog, and recoverer packages, each exposing a single New(...)
constructor.

The Middleware type alias moves to pkg/router. The splinter dependency
is dropped from go.mod; only errx remains.

BREAKING CHANGE: consumers must replace pkg/middleware imports with
the corresponding lightmux-contrib sub-packages. See README for the
new usage.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-26 14:02:54 +00:00

111 lines
3.4 KiB
Markdown

# lightmux
A small, idiomatic wrapper around Go 1.22+ `net/http.ServeMux` that adds method-named convenience methods, composable groups, and per-route middleware — without abandoning the stdlib pattern syntax.
## Installation
Requires Go 1.22 or later (uses `http.ServeMux`'s method+pattern syntax and `r.PathValue`).
```sh
go get git.juancwu.dev/juancwu/lightmux@v0.1.0
```
## Quick start
```go
package main
import (
"fmt"
"log"
"net/http"
"strconv"
"git.juancwu.dev/juancwu/lightmux"
)
func main() {
mux := lightmux.New()
mux.Get("/", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, "hello from lightmux")
})
mux.Get("/users/{id}", func(w http.ResponseWriter, r *http.Request) {
id, err := strconv.Atoi(r.PathValue("id"))
if err != nil {
http.Error(w, "bad id", http.StatusBadRequest)
return
}
fmt.Fprintf(w, "user %d\n", id)
})
api := mux.Group("/api")
api.Get("/ping", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, "pong")
})
log.Fatal(http.ListenAndServe(":8080", mux))
}
```
## Groups
`Group` returns a sub-mux that shares the underlying ServeMux but carries its own prefix and middleware stack. Groups can be nested.
```go
api := mux.Group("/api", authMiddleware)
api.Get("/users/{id}", getUser)
v1 := api.Group("/v1")
v1.Get("/ping", pong) // registered as "GET /api/v1/ping"
```
Middleware is snapshotted into the child at `Group()` time. Calling `parent.Use(mw)` *after* a `Group()` does not retroactively wrap the child's routes — register middleware before creating groups, or pass it to `Group(prefix, mw...)` explicitly.
## Middleware
There are two ways to attach middleware: chain-style with `Use`, or per-route as a variadic tail.
```go
mux := lightmux.New()
mux.Use(authMiddleware) // applies to every route registered after
mux.Get("/admin", adminHandler, requireAdmin) // requireAdmin only on this route
```
Order is outer → inner: `Use`-order first, then per-route mws. The outermost middleware runs first on the request and last on the response.
Middleware values are plain `func(http.Handler) http.Handler`, so any stdlib-compatible middleware works without an adapter.
An opinionated set of middlewares (request logging, panic recovery, real-IP resolution) lives in a sibling module — see [lightmux-contrib](https://git.juancwu.dev/juancwu/lightmux-contrib):
```sh
go get git.juancwu.dev/juancwu/lightmux-contrib
```
```go
import (
"git.juancwu.dev/juancwu/lightmux-contrib/realip"
"git.juancwu.dev/juancwu/lightmux-contrib/recoverer"
"git.juancwu.dev/juancwu/lightmux-contrib/requestlog"
)
mux.Use(recoverer.New(), realip.New(), requestlog.New(nil))
```
## Path parameters
lightmux is a thin wrapper, so path parameters work the stdlib way:
```go
mux.Get("/items/{name}", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, r.PathValue("name"))
})
```
## Notes
- `mux.Get("/", h)` registers `"GET /"`, which is a stdlib **subtree** pattern — it matches every unmatched path. Use `"/{$}"` to match only the literal root.
- Bad route registrations (invalid prefix, conflicting wildcards) panic at startup, matching stdlib `http.ServeMux` behavior. The `recoverer` middleware in [lightmux-contrib](https://git.juancwu.dev/juancwu/lightmux-contrib) handles panics that occur *inside* request handlers.
- Group prefixes apply to the path only — they never inject a host. Routes can still carry an explicit host via `Handle("GET host.com/path", h)`.