BunRouter is an extremely fast Golang router for Go with unique combination of features:
gin.Context
or echo.Context
).
And yes, it is fast - see the benchmark results.
BunRouter uses a slightly enhanced version of http.HandlerFunc that accepts a bunrouter.Request
and returns errors that you can handle with middlewares:
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:
:param
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.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(...)
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 error handling for details.
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
.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.