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!

Flow: A delightfully tiny but powerful HTTP router for Go

Published on:

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 and gorilla/mux.
  • Automatic handling of OPTIONS requests, like julienschmidt/httprouter.
  • Automatic handling of HEAD requests, like bmizerany/pat.
  • An Allow header is automatically set on all OPTIONS and 405 Method Not Allowed responses, like julienschmidt/httprouter.
  • Ability to map multiple HTTP methods to the same handler in one declaration, like gorilla/mux.


  • 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 and 405 Method Not Allowed responses are customizable.
  • Conflicting routes are permitted (e.g. /posts/:id and posts/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).

// 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.HandleFunc("/admin", exampleHandlerFunc3, "GET")

    // Groups can be nested.
    mux.Group(func(mux *flow.Mux) {

        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.