BunRouter is an extremely fast Golang router for Go with unique combination of features: Middlewares allow to extract common operations from HTTP handlers into reusable functions. Error handling allows to further reduce the size of HTTP handlers by handling errors in middlewares. Routes priority enables meaningful matching priority for routing rules: first static nodes, then named nodes, lastly wildcard nodes. net/http compatible API which means using minimal API without constructing huge wrappers that try to do everything: from serving static files to XML generation (for example, or ). gin.Context echo.Context And yes, it is fast - see the benchmark results. Quickstart BunRouter uses a slightly enhanced version of http.HandlerFunc that accepts a and returns errors that you can handle with middlewares: bunrouter.Request import "github.com/uptrace/bunrouter" router := bunrouter.New( bunrouter.Use(reqlog.NewMiddleware()), ) router.WithGroup("/api", func(g *bunrouter.Group) { g.GET("/users/:id", debugHandler) g.GET("/users/current", debugHandler) g.GET("/users/*path", debugHandler) }) func debugHandler(w http.ResponseWriter, req bunrouter.Request) error { // use req.Request to get *http.Request return bunrouter.JSON(w, bunrouter.H{ "route": req.Route(), "params": req.Params().Map(), }) } But don't worry, BunRouter supports classical HTTP handlers too. BunRouter supports the following param types in routing rules: is a named parameter that matches a single path segment (text between slashes). :param is a wildcard parameter that matches everything and must always be at the end of the route. *param Middlewares Middlewares allow you to extract common functionality into a reusable function, for example, here is how you can write a middleware that logs processed requests: func middleware(next bunrouter.HandlerFunc) bunrouter.HandlerFunc { // you can initialize the middleware here // Return the middleware. return func(w http.ResponseWriter, req bunrouter.Request) error { rec := httptest.NewRecorder() // Pass the recorder instead of http.ResponseWriter. if err := next(rec, req); err != nil { fmt.Printf("%s %s failed: %s\n", req.Method, req.Route(), err) // Discard the error. return nil } fmt.Printf("%s %s returned %d\n", req.Method, req.Route(), rec.Code) } } You can then install the middleware like this: router.Use(middleware).WithGroup(...) Error handling As you may have noticed, BunRouter's handlers return errors which you can then handle in middlewares: func errorHandler(next bunrouter.HandlerFunc) bunrouter.HandlerFunc { return func(w http.ResponseWriter, req bunrouter.Request) error { // Call the next handler on the chain to get the error. err := next(w, req) switch err := err.(type) { case nil: // no error case HTTPError: // already a HTTPError w.WriteHeader(err.statusCode) _ = bunrouter.JSON(w, err) default: httpErr := NewHTTPError(err) w.WriteHeader(httpErr.statusCode) _ = bunrouter.JSON(w, httpErr) } return err // return the err in case there other middlewares } } See for details. error handling Routes priority Routing rules have a matching priority that is based on node types and does not depend on routes definition order: Static nodes, for example, /users/ Named nodes, for example, . :id Wildcard nodes, for example, . *path The following routes are sorted by their matching priority from the highest to the lowest: . /users/list . /users/:id . /users/*path What's next? To get started, see the documentation and run examples. BunRouter comes with many plugins including OpenTelemetry instrumentation that enables distributed tracing and metrics. Using tracing, you can monitor performance using one of the open source tracing tools that work with OpenTelemetry. Many DataDog competitors also support OpenTelemetry. Besides, you can export metrics to Prometheus and visualize them using Grafana or a popular alternative.