Fast Golang router with error handling
Vladimir Mihailenco
Posted on April 17, 2022
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,
gin.Context
orecho.Context
).
And yes, it is fast - see the benchmark results.
Quickstart
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
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 error handling for details.
Routes priority
Routing rules have 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 Golang HTTP router docs and run examples.
BunRouter comes with many plugins including OpenTelemetry instrumentation that enables OpenTelemetry tracing and OpenTelemetry metrics.
Using tracing, you can monitor performance using one of the distributed tracing tools that work with OpenTelemetry. Many DataDog competitors also support OpenTelemetry.
Besides, you can export metrics to Prometheus and visualize them using Grafana alternatives.
Posted on April 17, 2022
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.