mux is a small routing layer on top of Go's standard net/http.ServeMux.
Since Go 1.22 the standard multiplexer already matches methods, path wildcards
({id}, {path...}), the exact trailing-slash marker {$}, host patterns and
the "most specific wins" precedence rule. mux does not reimplement any of that
- it adds the ergonomics the standard library leaves out: method helpers, prefix groups, middleware chains and an optional error-returning handler that pairs with goloop/resp.
The patterns you write are plain net/http.ServeMux patterns, not a custom
syntax. A Router is itself an http.Handler, so it drops into any net/http
server and composes with any middleware of the form
func(http.Handler) http.Handler.
- Method helpers -
Get,Post,Put,Patch,Delete,Options,Head, and the genericMethod. - Route groups and prefixes -
Route,Group,Mount,MountStrip. - Middleware chains -
Use,With,Chain, applied once at registration time. - Error-returning handlers -
GetE/PostE/... plus a centralErrorHandler, with a JSON 500 default viagoloop/resp. - Path parameters through the standard
r.PathValue, aliased asmux.Param. - Custom
404and405handlers via options; the standardAllowheader is preserved.
go get -u github.com/goloop/muximport "github.com/goloop/mux"Requires Go 1.25 or newer.
r := mux.New()
r.Use(requestID, recoverer, logger) // any func(http.Handler) http.Handler
r.Get("/users/{id}", func(w http.ResponseWriter, req *http.Request) {
fmt.Fprintf(w, "user %s", mux.Param(req, "id"))
})
r.Route("/api/v1", func(r *mux.Router) {
r.Use(auth)
r.Get("/orders/{id}", showOrder)
r.Post("/orders", createOrder)
})
http.ListenAndServe(":8080", r)Error-returning handlers keep the happy path clean and route failures through a single place:
r := mux.New(mux.WithErrorHandler(func(w http.ResponseWriter, req *http.Request, err error) {
resp.Error(w, http.StatusInternalServerError, "internal server error")
}))
r.GetE("/users/{id}", func(w http.ResponseWriter, req *http.Request) error {
return resp.JSON(w, resp.R{"id": mux.Param(req, "id")})
})Without WithErrorHandler, a returned error yields a generic JSON 500 through
goloop/resp.
mux never parses paths itself. Everything you can write in the standard
multiplexer works here unchanged:
| You write | Registered pattern |
|---|---|
r.Get("/users/{id}", h) |
GET /users/{id} |
r.Get("/files/{path...}") |
GET /files/{path...} |
r.Get("/exact/{$}", h) |
GET /exact/{$} |
r.Handle("GET a.com/x", h) |
GET a.com/x |
Because the standard mux applies "most specific wins", route registration order
does not change matching. Conflicting patterns panic, exactly as net/http does.
Middleware added with Use or With are captured when a route is registered,
so they can read path values via mux.Param. They do not run for unmatched
requests (404) or method mismatches (405). For application-wide middleware
that must run for every request, wrap the router - it is a plain http.Handler:
handler := requestID(recoverer(logger(r)))
http.ListenAndServe(":8080", handler)Call Use before registering the routes it should cover: a route added earlier
will not see middleware added later.
- Full reference and recipes: DOC.md · DOC.UK.md
- Package API: pkg.go.dev/github.com/goloop/mux
- Changes between versions: CHANGELOG.md
Contributions are welcome. Please run go test ./..., go vet ./... and
gofmt -l . before submitting a pull request.
mux is released under the MIT License. See LICENSE.