delete runable example and update readme

This commit is contained in:
juancwu 2026-04-26 01:56:25 +00:00
commit 24d8590b56
2 changed files with 108 additions and 47 deletions

109
README.md
View file

@ -1,3 +1,110 @@
# lightmux
Simple router/mux for Go projects.
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
```
## Quick start
```go
package main
import (
"fmt"
"log"
"net/http"
"strconv"
"git.juancwu.dev/juancwu/lightmux"
"git.juancwu.dev/juancwu/lightmux/pkg/middleware"
)
func main() {
mux := lightmux.New()
mux.Use(middleware.Recoverer, middleware.Logger)
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(middleware.Recoverer, middleware.Logger) // 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.
## Built-in middleware
The `pkg/middleware` package ships:
- **`Logger`** — emits a structured `http.request` record (method, path, status, duration) via [splinter](https://git.juancwu.dev/juancwu/splinter)'s default logger.
- **`LoggerWith(*splinter.Logger)`** — same, but routes records through the supplied splinter logger instead of the default.
- **`Recoverer`** — catches panics inside handlers, wraps the value with [errx](https://git.juancwu.dev/juancwu/errx) under op `middleware.Recoverer`, logs it with the stack, and writes a 500 response.
```go
custom := splinter.New(splinter.WithStream(...))
mux.Use(middleware.Recoverer, middleware.LoggerWith(custom))
```
## 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. `Recoverer` 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)`.