Migration Guide
This guide shows how to migrate an existing Go application to MuxMaster from three common routers: gorilla/mux, chi, and httprouter.
MuxMaster implements http.Handler and uses the same http.HandlerFunc signature as the standard library. Most migrations consist of replacing route registration calls — no handler code changes are required.
Table of Contents
From gorilla/mux
gorilla/mux is archived and no longer maintained. MuxMaster provides a compatible API with dramatically better performance.
Route registration
// gorilla/mux
r := mux.NewRouter()
r.HandleFunc("/users", listUsers).Methods("GET")
r.HandleFunc("/users", createUser).Methods("POST")
r.HandleFunc("/users/{id}", getUser).Methods("GET")
r.HandleFunc("/users/{id}", updateUser).Methods("PUT")
r.HandleFunc("/users/{id}", deleteUser).Methods("DELETE")
// MuxMaster
mux := muxmaster.New()
mux.GET("/users", listUsers)
mux.POST("/users", createUser)
mux.GET("/users/:id", getUser)
mux.PUT("/users/:id", updateUser)
mux.DELETE("/users/:id", deleteUser)
Path parameters
// gorilla/mux
id := mux.Vars(r)["id"]
// MuxMaster
id := muxmaster.PathParam(r, "id")
// or
id := muxmaster.ParamsFromContext(r.Context()).Get("id")
Regex-constrained parameters
gorilla/mux regex syntax and MuxMaster syntax differ slightly:
// gorilla/mux — regex inside curly braces after colon
r.HandleFunc("/users/{id:[0-9]+}", getUser)
// MuxMaster — same syntax, compatible
mux.GET("/users/{id:[0-9]+}", getUser)
Subrouters
// gorilla/mux
api := r.PathPrefix("/api/v1").Subrouter()
api.Use(requireAPIKey)
api.HandleFunc("/users", listUsers).Methods("GET")
// MuxMaster
api := mux.Group("/api/v1")
api.Use(requireAPIKey)
api.GET("/users", listUsers)
Middleware
// gorilla/mux
r.Use(loggingMiddleware)
// MuxMaster — identical
mux.Use(loggingMiddleware)
Starting the server
// gorilla/mux
http.ListenAndServe(":8080", r)
// MuxMaster — identical
http.ListenAndServe(":8080", mux)
From chi
chi and MuxMaster share a very similar API. Most migrations require minimal changes.
Route registration
// chi
r := chi.NewRouter()
r.Get("/users", listUsers)
r.Post("/users", createUser)
r.Get("/users/{id}", getUser)
r.Put("/users/{id}", updateUser)
r.Delete("/users/{id}", deleteUser)
// MuxMaster — lowercase → uppercase method names
mux := muxmaster.New()
mux.GET("/users", listUsers)
mux.POST("/users", createUser)
mux.GET("/users/:id", getUser) // chi uses {id}, MuxMaster uses :id
mux.PUT("/users/:id", updateUser)
mux.DELETE("/users/:id", deleteUser)
chi uses {param} syntax; MuxMaster uses :param syntax. Regex constraints use the same {param:regexp} syntax in both.
Path parameters
// chi
id := chi.URLParam(r, "id")
// MuxMaster
id := muxmaster.PathParam(r, "id")
Route groups
// chi
r.Route("/api/v1", func(r chi.Router) {
r.Use(requireAPIKey)
r.Get("/users", listUsers)
r.Post("/users", createUser)
})
// MuxMaster — nearly identical
mux.Route("/api/v1", func(g *muxmaster.Group) {
g.Use(requireAPIKey)
g.GET("/users", listUsers)
g.POST("/users", createUser)
})
Inline scoped middleware
// chi
r.With(requireAdmin).Delete("/users/{id}", deleteUser)
// MuxMaster — identical
mux.With(requireAdmin).DELETE("/users/:id", deleteUser)
Mounting sub-routers
// chi
r.Mount("/admin", adminRouter())
// MuxMaster — identical
mux.Mount("/admin", adminRouter())
chi Middleware
chi's middleware package uses the same func(http.Handler) http.Handler signature. All chi middleware is compatible with MuxMaster:
import chimiddleware "github.com/go-chi/chi/v5/middleware"
mux.Use(chimiddleware.Logger)
mux.Use(chimiddleware.Recoverer)
You can migrate gradually: keep using chi middleware while replacing the router.
From httprouter
httprouter has a different handler signature: func(http.ResponseWriter, *http.Request, httprouter.Params). Migrating to MuxMaster requires updating handler signatures to use the standard http.HandlerFunc.
Handler signature
// httprouter
func getUser(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
id := ps.ByName("id")
// ...
}
router := httprouter.New()
router.GET("/users/:id", getUser)
// MuxMaster — standard net/http signature
func getUser(w http.ResponseWriter, r *http.Request) {
id := muxmaster.PathParam(r, "id")
// ...
}
mux := muxmaster.New()
mux.GET("/users/:id", getUser)
For a large codebase, you can write a thin adapter to avoid rewriting all handlers at once:
// Adapter: wraps a httprouter-style handler as a MuxMaster handler
func adapt(h func(http.ResponseWriter, *http.Request, httprouter.Params)) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
ps := muxmaster.ParamsFromContext(r.Context())
// Convert muxmaster.Params to httprouter.Params
hrps := make(httprouter.Params, len(ps))
for i, p := range ps {
hrps[i] = httprouter.Param{Key: p.Key, Value: p.Value}
}
h(w, r, hrps)
}
}
mux.GET("/users/:id", adapt(getUser))
Route registration
// httprouter
router := httprouter.New()
router.GET("/users", listUsers)
router.POST("/users", createUser)
router.GET("/users/:id", getUser)
// MuxMaster — identical route patterns, different handler type
mux := muxmaster.New()
mux.GET("/users", listUsers)
mux.POST("/users", createUser)
mux.GET("/users/:id", getUser)
Custom error handlers
// httprouter
router.NotFound = myNotFoundHandler
router.MethodNotAllowed = myMethodNotAllowedHandler
router.PanicHandler = myPanicHandler
// MuxMaster — identical
mux.NotFound = myNotFoundHandler
mux.MethodNotAllowed = myMethodNotAllowedHandler
mux.PanicHandler = myPanicHandler
From net/http ServeMux
net/http.ServeMux does not support path parameters or middleware. Migration to MuxMaster adds these capabilities without changing handler code.
// net/http
mux := http.NewServeMux()
mux.HandleFunc("/users", listUsers)
mux.HandleFunc("/users/", userDetail) // catches /users/anything
// MuxMaster — explicit path parameters
mux := muxmaster.New()
mux.GET("/users", listUsers)
mux.GET("/users/:id", userDetail)
Handler code that uses r.URL.Path to extract the "parameter" can be simplified:
// net/http — manual extraction
func userDetail(w http.ResponseWriter, r *http.Request) {
id := strings.TrimPrefix(r.URL.Path, "/users/")
// ...
}
// MuxMaster — automatic extraction
func userDetail(w http.ResponseWriter, r *http.Request) {
id := muxmaster.PathParam(r, "id")
// ...
}
Common Adjustments
Parameter syntax
| Router | Named param | Catch-all | Regex param |
|---|---|---|---|
| gorilla/mux | {id} |
— | {id:[0-9]+} |
| chi | {id} |
* |
{id:[0-9]+} |
| httprouter | :id |
*id |
— |
| MuxMaster | :id |
*id |
{id:[0-9]+} |
Middleware compatibility
Any middleware with the signature func(http.Handler) http.Handler is compatible with MuxMaster. This covers:
- All chi middleware (
github.com/go-chi/chi/v5/middleware) - All gorilla handlers with that signature
- Most popular community middleware packages
Trailing slash behaviour
MuxMaster redirects trailing slashes by default (RedirectTrailingSlash = true). If your application registers both /users and /users/ as separate routes, disable this:
mux.RedirectTrailingSlash = false
See Also
- Routing — complete pattern syntax reference
- Middleware — built-in and custom middleware
- Groups — organizing routes with groups and sub-routers
- Configuration — all router options
Upstream source
The authoritative list of MuxMaster releases, breaking changes, and deprecations referenced above lives in CHANGELOG.md in the upstream repository.
Common questions
How do I migrate a chi router to MuxMaster?
Replace chi.NewRouter() with mux.New(), the route helpers (Get, Post, etc.) keep their names, and chi.URLParam(r, "id") becomes mux.Param(r, "id"). Most chi middlewares run unchanged because both routers expose the same func(http.Handler) http.Handler shape.
How do I migrate a gorilla/mux router?
Replace mux.NewRouter() with muxmaster.New() (alias the import to keep call sites short) and rename r.HandleFunc("/path", h).Methods("GET") to m.GET("/path", h). Path parameters use the same colon-prefix syntax in both libraries; named regex constraints have no equivalent and must be moved into handler-level validation.
Which gorilla/mux features have no MuxMaster equivalent?
Schemes/host matchers, named regex constraints, and the Middleware trie are intentionally omitted to keep the router small and zero-allocation. Schemes and hosts belong in the reverse proxy or a Pre middleware; regex constraints belong in handler-level validation; middleware trie behaviour is achieved via groups and Use.