Flow: A delightfully tiny but powerful HTTP router for Go
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
chiandgorilla/mux. - Automatic handling of
OPTIONSrequests, likejulienschmidt/httprouter. - Automatic handling of
HEADrequests, likebmizerany/pat. - An
Allowheader is automatically set on allOPTIONSand405 Method Not Allowedresponses, 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 Foundand405 Method Not Allowedresponses are customizable. - Conflicting routes are permitted (e.g.
/posts/:idandposts/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.
