Routing
Register handlers with method helpers, nest routes with groups, and add scoped or inline middleware.
Router is chi underneath, with method helpers, groups, and middleware. Handlers use plain net/http signatures and never import chi, so the router stays the only thing that knows about it.
Registering handlers
NewRouter builds an empty router. Register a handler with the method helper that matches its verb, or with Handle for anything else.
r := server.NewRouter()
r.Get("/health", healthHandler)
r.Post("/items", createItem)
r.Put("/items/{id}", replaceItem)
r.Delete("/items/{id}", deleteItem)
r.Patch("/items/{id}", updateItem)
// Handle takes an explicit method and an http.Handler for anything the
// helpers do not cover.
r.Handle("OPTIONS", "/items", preflight)Patterns use chi's syntax, which mirrors the stdlib {name} placeholder for a single segment and adds a trailing /* for a catch-all. Read those values back with the params helpers.
Groups
Group mounts a set of routes under a shared prefix. Middleware added inside the group applies only to routes registered on it, so a group is how you scope auth or logging to one part of the tree.
r.Group("/api", func(api *server.Router) {
api.Use(server.AuthMiddleware(extractClaims, logger)) // scoped to /api
api.Get("/items/{id}", func(w http.ResponseWriter, req *http.Request) {
server.WriteJSON(w, http.StatusOK, map[string]string{"id": server.Param(req, "id")})
})
})Groups nest, so you can build a deeper tree by calling Group again inside the callback.
Middleware
Use appends middleware to a router's scope. Every request that the scope dispatches flows through the chain, including 404s, 405s, and CORS preflights.
r.Use(server.SlogMiddleware(server.SlogConfig{}, logger)) // applies to every routeWarning
chi panics if Use is called after a route is registered on the same scope, so add a scope's middleware before its routes.
For a one-off, With returns a sub-router that carries extra inline middleware without mutating the parent.
r.With(rateLimit).Post("/expensive", expensiveHandler)Registering a route table
When routes are described as data rather than call sites, collect them as a []Route and hand the slice to Register. It walks the table and registers each entry through the HandleRouter interface that *Router satisfies.
routes := []server.Route{
{Method: "GET", Pattern: "/health", Handler: healthHandler},
{Method: "POST", Pattern: "/items", Handler: createItem},
}
server.Register(r, routes)Inspecting the routes
LogRoutes walks every registered route and logs its method and pattern, which is a quick way to confirm the tree came out the way you expected. Server.Start calls it for you on startup.