Last year I wrote a new HTTP router for Go called Flow
. I've been using it in production on this site and in a couple of other projects since, and I'm pretty happy with how it's working out so decided to share it a bit more widely.
My aim with Flow
was to bring together my favourite features from other popular routers that I frequently used. It has:
- A very small and readable codebase (approx. 160 LOC) with pattern-matching logic similar to
matryer/way
. - Middleware management like
chi
— including the ability to create route 'groups' which use different middleware. - Optional regexp support for tighter pattern matching, similar to
chi
andgorilla/mux
. - Automatic handling of
OPTIONS
requests, likejulienschmidt/httprouter
. - Automatic handling of
HEAD
requests, likebmizerany/pat
. - An
Allow
header is automatically set on allOPTIONS
and405 Method Not Allowed
responses, likejulienschmidt/httprouter
. - Ability to map multiple HTTP methods to the same handler in one declaration, like
gorilla/mux
.
Additionally:
- It has a very small API (see the Go docs) so there's not much to learn.
- It's designed to work nicely with
http.Handler
,http.HandlerFunc
, and the standard Go middleware pattern. - The handlers for
404 Not Found
and405 Method Not Allowed
responses are customizable. - Conflicting routes are permitted (e.g.
/posts/:id
andposts/new
), with routes matched in the order that they are declared. - It has zero dependencies.
Below is a quick example of the syntax, and if you like the look of it you can check out the full README on GitHub.
mux := flow.New()
// The Use() method can be used to register middleware. Middleware declared at
// the top level will used on all routes (including error handlers and OPTIONS
// responses).
mux.Use(exampleMiddleware1)
// Routes can use multiple HTTP methods.
mux.HandleFunc("/profile/:name", exampleHandlerFunc1, "GET", "POST")
// Optionally, regular expressions can be used to enforce a specific pattern
// for a named parameter.
mux.HandleFunc("/profile/:name/:age|^[0-9]{1,3}$", exampleHandlerFunc2, "GET")
// The wildcard ... can be used to match the remainder of a request path.
// Notice that HTTP methods are also optional (if not provided, all HTTP
// methods will match the route).
mux.Handle("/static/...", exampleHandler)
// You can create route 'groups'.
mux.Group(func(mux *flow.Mux) {
// Middleware declared within in the group will only be used on the routes
// in the group.
mux.Use(exampleMiddleware2)
mux.HandleFunc("/admin", exampleHandlerFunc3, "GET")
// Groups can be nested.
mux.Group(func(mux *flow.Mux) {
mux.Use(exampleMiddleware3)
mux.HandleFunc("/admin/passwords", exampleHandlerFunc4, "GET")
})
})
A note on performance
I haven't done any benchmarking against other routers, so I can't speak about the relative performance of Flow
. What I can say it has been plenty fast enough for all of my use-cases so far and not a hot spot when profiling my applications under load.
If you enjoyed this post...
You might like to check out my other Go tutorials on this site, or if you're after something more structured, my books Let's Go and Let's Go Further cover how to build complete, production-ready web apps and APIS with Go.
Not sure how to structure your Go web application?
My new book guides you through the start-to-finish build of a real world web application in Go — covering topics like how to structure your code, manage dependencies, create dynamic database-driven pages, and how to authenticate and authorize users securely.
Take a look!