Aaron’s Desk Chair Adventures

Code. Engineering Management. Fun.

Go API Versioning with Gorilla Mux

Code-Based Versioning

Recently I had a problem where I needed to implement API versioning in code. The versions would share the vast majority of routes/handlers and I found that I wanted to re-use the base routes and modifying them slightly between versions. I imagined using Gorilla mux router as the base with all the routes and then creating a new router for v1, v2, etc that would ‘inherit’ the base routes and only modify a few. That would maximize code-reuse as I’d only have to modify the latest route in one place for many versions to receive the benefit.

I searched around Google, Stack Overflow, and the Gorilla mux documentation about attaching a subrouter to a route and similar terms, but I struggled to find a working solution. I found a few useful posts and here’s the result:

package main

import (


// AnotherHandlerLatest is the newest version of AnotherHandler
func AnotherHandlerLatest(w http.ResponseWriter, r *http.Request) {
        fmt.Fprintf(w, "hello from AnotherHandlerLatest.")

// ExampleHandlerLatest is the newest version of ExampleHandler
func ExampleHandlerLatest(w http.ResponseWriter, r *http.Request) {
        fmt.Fprintf(w, "hello from ExampleHandlerLatest.")

// ExampleHandlerV1 is a v1-compatible version of ExampleHandler
func ExampleHandlerV1(w http.ResponseWriter, r *http.Request) {
        fmt.Fprintf(w, "Hello from ExampleHandlerv1.")

// AddV1Routes takes a router or subrouter and adds all the v1
// routes to it
func AddV1Routes(r *mux.Router) {
        r.HandleFunc("/example", ExampleHandlerV1)

// AddV2Routes takes a router or subrouter and adds all the v2
// routes to it, note that these should probably match
// AddRoutes(r *muxRouter) alternatively you can do
// var AddV2Routes = AddRoutes
func AddV2Routes(r *mux.Router) {

// AddRoutes takes a router or subrouter and adds all the latest
// routes to it
func AddRoutes(r *mux.Router) {
        r.HandleFunc("/example", ExampleHandlerLatest)
        r.HandleFunc("/example2", AnotherHandlerLatest)

func main() {
        router := mux.NewRouter()

        // latest

        // v1

        // v2

The end result of this is that /example and /v2/example point at the same handler, but /v1/example points at a different handler. When a new version is created, I rename HandlerLatest to HandlerVn where n is the previous newest version. I add it to the AddVnRoutes, create an AddVn+1Routes and new version is made! I can easily deprecate older versions (and their associated handlers) as needed.