Sometimes, you just need to make a fast API. It doesn’t need to be big, it just needs to be fast and not take up half of your server’s memory.

This is what happened when the company I work for suddenly started hitting the rate limit that bit.ly recently started enforcing for on their URL shortening.

We needed to remedy this problem as soon as possible. We’re very dependent on this service, so we either needed to build this fast or pay up a huge yearly fee.

Requirements:

  • Very fast
  • Easily built and maintainable
  • Stays up, without maintenance for several months
  • Low memory footprint

We’re a rails shop, but doing this in rails or Sinatra would be quite slow, require a bunch of memory (multiple instances to handle concurrent requests) and would probably need to be looked after. So I opted for something a bit simpler.


Gorilla mux

I looked a bit online, and all the benchmarks seemed to point to this framework. It’s a small, but powerful addition on top of the standard Go library http multiplexer. Mostly, it’s just a router.

A really simple application would look like this:

func main() {
    router := mux.NewRouter()
    router.HandleFunc("/", HomeHandler)
    router.HandleFunc("/blog", BlogHandler)
    router.HandleFunc("/products/{id}", ProductHandler)
    http.Handle("/", router)
}

Even if you’re not familiar with Go, you can see that it just handles certain routes and pushes it to specific handlers. The handlers are just methods with this definition:

func HomeHandler(w http.ResponseWriter, r *http.Request) {
 // Code!
})

As you can also guess, {id} will be bound to anything passed into that section of the URL and will be accessible through the mux.Vars map.

vars := mux.Vars(request)
key := vars["id"]

Negroni

Negroni is the second piece of this amazing combo. It’s a middleware manager that allows you to bring in some extra pieces in your server pipeline. It integrates with gorilla mux like so:

router := mux.NewRouter()
router.HandleFunc("/", HomeHandler)

n := negroni.New(Middleware1, Middleware2)
// Or use a middleware with the Use() function
n.Use(Middleware3)
// router goes last
n.UseHandler(router)

n.Run(":3000")

Again, middlewares are just functions with this definition:

func MyMiddleware(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
  // do some stuff before
  next(rw, r)
  // do some stuff after
}

This allows you to place some code between your request and your handler. You can even interrupt any further processing if you want to. For example: You get an unauthenticated request and you just want to respond with a 401/403 and be done with the request. The call to next brings it to the next step in the middleware pipeline.

This package lets you add things like stats easily in your process without disturbing any of your own code. Negroni comes with basic middleware like panic recovery, logging and static file serving. You can get all of it by just using Negroni.Classic()

The really nice part of this combo is that you can have specific middleware running on only parts of your API. Let’s take this example:

func main() {
	router := BasicRouter()
	apiRouter := ApiRouter()
    pagesRouter := PagesRouter()

	n := negroni.Classic()

	router.PathPrefix("/api").Handler(negroni.New(
		negroni.HandlerFunc(AuthenticateRequest),
		negroni.Wrap(apiRouter),
	))

    router.PathPrefix("/pages").Handler(negroni.New(
         negroni.Wrap(pagesRouter),
    ))

	n.UseHandler(router)
	n.Run("0.0.0.0:9292")
}

In this case, I just run my request authentication on my API and I leave my pages alone. ApiRouter is a gorilla router instance. I also found having a router per path prefix kept the application a bit more rails-like and the files more manageable.



canoe

If you’d like to feel the speed of an API built with the gorilla/negroni combo, I decided to open source the URL shortener, named canoe that I built using this stack. You can find it here.

Feel free to fork it and send me pull request. I’d also love to hear some feedback.