Go package net/http provides a lot of things so that we can build web apps without that much code. But the routing capabilities of http.Handle(pattern, handler) is very basic. So basic you can only have static routes. That's why we need to use a better (3rd party) router, but there's a lot of routers with different features and we can easily get lost and not able to choose which one to use.

What is the best router in Go?

When beginning in a new language, it's scary to see 10 different libraries doing the same thing. You don't really know the best practices and don't know which one to use. There's already a lot of routers in Go and we want one that would be fast, memory efficient and simple to use. Here's an overview of routers I think are representative of what Go developers use the most.

There's a benchmark comparing speed and memory consumption of different routers, you may want to look at it.

gorilla/mux: The full-fledged router

gorilla/mux is the most popular router, but it is also slow and memory hungry. It is also the most feature packed router. You can have URL params with regex constraints:

r := mux.NewRouter()
r.HandleFunc("/teas/{category}/", TeasCategoryHandler)
r.HandleFunc("/teas/{category}/{id:[0-9]+}", TeaHandler)

You can match a route with a method:

r.Methods("GET", "HEAD").HandleFunc("/teas/{category}/", TeasCategoryHandler)

But unlike other routers, there's also a lot of other built-in matchers. You can match routes with an host (like a subdomain), a prefix, a schema (http, https, etc), HTTP headers, query params, and if it doesn't suit you you can easily create a custom matcher like this:

r.MatcherFunc(func(r *http.Request, rm *RouteMatch) bool {
  return r.ProtoMajor == 0
})

Inside your handler, you can get the URL Params from mux.Vars(request) which is basically a context like gorilla/context that we saw in the previous part.

func myHandler(w http.ResponseWriter, r *http.Request) {
  vars := mux.Vars(r)
  category := vars["category"]
}

This solution is compatible with http.Handler interface and like I already said, it's important because the more apps we make the more likely we will need to share middlewares/handlers the more it has to follow the same principles.

Pros: Lots of features, you can create complex rules very easily. And it's compatible with http.Handler

Cons: Slow and memory hungry. Might not be suitable if what you need is speed.

httprouter: The fastest

httprouter is the fastest router and the benchmark provided in the note above has been made by the same developer. It is simpler than gorilla/mux, you can't make constraints nor regex in your routes. I think it's fine when making REST APIs, but for complex view routes the simplicity of this router can be limiting. It is not compatible with http.Handler, it defines a new interface taking a 3rd argument for the URL params.

func Index(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
    fmt.Fprint(w, "Welcome!\\n")
}

func Hello(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
    fmt.Fprintf(w, "hello, %s!\\n", ps.ByName("name"))
}

func main() {
    router := httprouter.New()
    router.GET("/", Index)
    router.GET("/hello/:name", Hello)
}

This problem can be overcome by putting URL params into a context and using standard http.Handler (more on that later). We lose a bit of performance but it still will be the fastest router.