Fast Golang router with error handling

vmihailenco

Vladimir Mihailenco

Posted on April 17, 2022

Fast Golang router with error handling

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 or echo.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(),
    })
}
Enter fullscreen mode Exit fullscreen mode

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)
    }
}
Enter fullscreen mode Exit fullscreen mode

You can then install the middleware like this:

router.Use(middleware).WithGroup(...)
Enter fullscreen mode Exit fullscreen mode

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
    }
}
Enter fullscreen mode Exit fullscreen mode

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:

  1. Static nodes, for example, /users/
  2. Named nodes, for example, :id.
  3. 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.

đź’– đź’Ş đź™… đźš©
vmihailenco
Vladimir Mihailenco

Posted on April 17, 2022

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related