Which Go router should I use? (with flowchart)
When you start to build web applications with Go, one of the first questions you'll probably ask is "which router should I use?".
It's not an easy question to answer, either. There are probably more than 100 different routers available, all with different APIs, features, and behaviors. So for this blog post I've evaluated 30 popular ones, and created a shortlist of the best options along with a flowchart that you can use to help guide your choice.
Before we start, a few notes on terminology:
- By supports method-based routing I mean that the router makes it easy to dispatch a HTTP
request to different handlers based on the request method (
"GET"
,"POST"
, etc). - By supports variables in URL paths I mean that the router makes it easy to declare routes like
/movies/{id}
where{id}
is a dynamic value in the URL path. - By supports regexp route patterns I mean that the router makes it easy to declare routes like
/movies/{[a-z-]+}
where[a-z-]+
is a required regexp match in the URL path. - By supports host-based routes I mean that the router makes it easy to dispatch a HTTP request
to different handlers based on the URL host (like
www.example.com
) rather than just the URL path. - By supports custom routing rules I mean that the router makes it easy to add custom rules for
routing requests (such as routing to different handlers based on IP address, or the value in an
Authorization
header). - By conflicting routes I mean when you register two (or more) route patterns that potentially
match the same request URL path. For example, if you register the routes
/blog/{slug}
and/blog/new
then a HTTP request with the path/blog/new
matches both these routes.
Shortlisted routers
There are four different routers that make the shortlist. They are http.ServeMux
, julienschmidt/httprouter
, go-chi/chi
and gorilla/mux
. All four are well-tested, well-documented,
and actively maintained. They (mostly) have stable APIs, and are compatible with http.Handler
,
http.HandlerFunc
, and the standard middleware pattern.
In terms of speed, all four routers are fast enough for (almost) every application and I recommend choosing between them based on the specific features that you need rather than performance. I've personally used all four in production applications at different times and have been happy with them.
http.ServeMux
I'll start by saying that if you can use http.ServeMux
, you probably should.
As part of the Go standard library, it's very battle tested and well documented. Using it means that you don't need
to import any third-party dependencies, and most other Go developers will also be familiar how it works. The Go 1
compatibility promise also means that you should be able to rely on http.ServeMux
working the same way
in the long-term. All of those things are big positives in terms of application maintenance.
Unlike most other routers it also supports host-based routes, incoming request URLs are automatically sanitized, and the way that it matches routes is smart too: longer route patterns always take precedence over shorter ones. This has the nice side-effect that you can register patterns in any order and it won't change how your application behaves.
The two main limitations of http.ServeMux
are that it doesn't support method-based routing or variables
in URL paths. But the lack of support for method-based routing isn't always a good reason to avoid it — it's
quite easy to work around with some code like this:
In those few lines you've effectively got method-based routing, along with custom 404
and
405
responses and support for OPTIONS
requests. That's lot more than you get with many
third-party routers.
Over time, I've come to realize that http.ServeMux
has a lot of positives and it's perfectly sufficient
in many cases. In fact, the only time I'd recommend not using it is when you need support for variables in
URL paths or custom routing rules. In those cases, trying to work with http.ServeMux
can get a bit
hairy and I think it's generally better to opt for a third-party router.
julienschmidt/httprouter
I think that julienschmidt/httprouter
is about
as close to 'perfect' as any third-party router gets in terms of its behavior and compliance with the HTTP specs. It
supports method-based routing and dynamic URLs, automatically handles 404
and 405
responses correctly (including allowing you to set custom handlers for these responses), and also automatically handles OPTIONS
requests for you too.
It doesn't support host-based routes, custom routing rules or regexp route patterns. It's also important to note that
httprouter
does not allow conflicting routes, like /post/create
and
/post/:id
. That's objectively a good thing because it helps avoid bugs, but may be a problem if you
need to use conflicting routes (e.g. to align with the routes used by an existing system).
One downside of httprouter
is that the API and documentation is a bit confusing. The package was first
published prior to the introduction of request context in Go 1.7, and lot of the current API still exists in order
to support these older versions of Go. Nowadays, you can write your handlers using regular http.Handler
and http.HandlerFunc
signatures and all you need is the router.Handler()
and
router.HandlerFunc()
methods to register them. For example:
go-chi/chi
The go-chi/chi
package supports method-based routing, variables in URL paths, and
regexp route patterns. Like httprouter
, it automatically handles 404
and 405
responses and allows you to set custom handlers for them.
But my favorite thing about chi
is that you can create 'groups' of routes which use specific middleware,
like in the code snippet below. This is really useful in larger applications where you have lots of middleware and
routes to manage.
It's important to note that chi
does allow conflicting routes, with routes being matched in the
order that they are declared.
Two downsides of chi
are that it doesn't automatically handle OPTIONS
requests, and an
Allow
header won't be set if you are using a custom handler for 405
responses. If you're building a web application or a
private API, then those things probably aren't a big problem — but if you're building a public API it's
something to be aware of. Like httprouter
, it also doesn't support host-based routes.
As a note of caution, there have been 5 major version increments for chi
in the past 6 years —
most of which include breaking changes. History isn't necessarily a predictor of the future, but does suggest that
backward-compatibility and not making breaking changes is less of a priority for chi
than some of the
other routers on this shortlist. In contrast httprouter
and gorilla/mux
haven't made any
breaking changes in this time.
gorilla/mux
The gorilla/mux
package is perhaps the most famous Go router, and with good reason.
It's packed full of features, including support for method-based routing, dynamic URLs, regexp route patterns, and
host-based routing. Importantly, it's the only router on this shortlist which supports custom routing rules
and route 'reversing' (like you get in Django, Rails or Laravel). It also allows you to set custom
handlers for 404
and 405
responses. Another nicety of Gorilla mux is that you can match multiple HTTP methods to the same handler in a single line of code (all the other routers require you to create individual routes for each method).
It's downsides are basically the same as chi
— it doesn't automatically handle
OPTIONS
requests like httprouter
does, and it doesn't automatically include an Allow
header
in all 405
responses. Again, like chi
, it does allow conflicting routes, with routes
being matched in the order that they are declared.
Given that the downsides of chi
and gorilla/mux
are similar, picking between the two is
fairly straightforward: opt for gorilla/mux
if you need support for custom routing rules, host-based
routing or route 'reversing'. If you don't need those 'advanced' features then it's probably better to go with
chi
because of the nice features you get for managing middleware, especially if you're building a large
application.
Honorable mentions
Two other routers that I think are worth mentioning are bmizerany/pat
and matryer/way
. I have a soft spot for both of these because
they are deliberately simple. They have small APIs and very clear and understandable code, which makes it
easy to grasp exactly how the router is working behind the scenes. The code behind matryer/way
in
particular is well worth a read.
While they're less featureful than the other routers in the shortlist, I think that their simplicity makes them a good fit for use in tutorials (especially tutorials aimed at new gophers), or as a starting point/inspiration if you fancy building your own custom router.
If you like small and simple routers, you might also want to consider alexedwards/flow
. I wrote this a couple of years ago to brings together my favorite features from all the routers above, and there's a short introductory post about it here.
Flowchart
With the various pros and cons and supported features in mind, this flowchart should help you choose between the four shortlisted routers.
Other routers
The other routers that I evaluated are listed below, along with a short note to explain why they didn't made the shortlist.
Repository | Notes |
---|---|
celrenheit/lion | Currently unmaintained. |
claygod/Bxog | Currently unmaintained. |
clevergo/clevergo | Uses custom handler signature (not http.Handler or http.HandlerFunc). |
dimfeld/httptreemux | Doesn’t fully support http.Handler. Requires middleware for setting custom 404/405 handlers. |
donutloop/mux | Currently unmaintained. |
gernest/alien | Currently unmaintained. |
go-ozzo/ozzo-routing | Uses custom handler signature (not http.Handler or http.HandlerFunc). |
go-playground/lars | Currently unmaintained. |
go-zoo/bone | Good, but has similar use case to chi (which offers more). Incomplete tests. |
go101/tinyrouter | Verbose route declarations. Doesn’t automatically send 405 responses. |
gocraft/web | Currently unmaintained. |
goji/goji | Slightly unusual, but flexible, API which supports custom matchers. Requires middleware for setting custom 404/405 handlers. Good, but I think gorilla/mux offers similar features and is easier to use. |
goroute/route | Uses custom handler signature (not http.Handler or http.HandlerFunc). |
gowww/router | Good, but has similar use case to chi (which offers more). No way to set custom 405 handler. |
GuilhermeCaruso/bellt | No way to set custom 404 or 405 handlers. |
husobee/vestigo | Currently unmaintained. Only supports http.HandlerFunc. |
naoina/denco | Currently unmaintained. |
nbari/violetear | Good, but has similar use case to chi (which offers more). Wraps http.ResponseWriter with own custom type, which may cause problems in some cases. |
nbio/hitch | Lacking documentation. |
nissy/bon | Currently unmaintained. |
razonyang/fastrouter | Currently unmaintained. |
rs/xmux | Currently unmaintained. Uses custom handler signature (not http.Handler or http.HandlerFunc). |
takama/router | Currently unmaintained. |
vardius/gorouter | Good, but has similar use case to chi (which offers more). Four major versions in 5 years suggests the API may not be reliable. |
VividCortex/siesta | Good, but has similar use case to chi (which offers more). No way to set custom 405 handler. |
xujiajun/gorouter | Currently unmaintained. |