How I structure production grade REST API’s in Golang.

Anthony Alaribe
ITNEXT
Published in
4 min readJun 23, 2018

--

Image result for rest api meme
One does not simply create an api in golang… Or do they?

There is a myth that API’s written in golang cannot be simple and idiomatic, like in other languages. Actually, I’ve come across a lot of REST API codebases that turned into a complicated mess with so many abstractions with ended up hurting both readability and maintainability.

With this series, we will walk through how to build a production grade todo list rest api, which will grow organically, starting with the necessities, like code structure and routing, then going on to add a both a mongo db and a badger data storage layer, and then an authentication layer.

We will be using the chi router for this series.

chi router

Why Chi? Why not standard library, or Gin, or router-x?

Well, it doesn’t matter so much. The concepts that will be discussed in this series will work irrespective of what router you use. But there is a check list of things that make me regard Chi-router as a superior choice than most alternatives:

  • 100% compatible with net/http — use any http or middleware pkg in the ecosystem that is also compatible with net/http
  • Designed for modular/composable APIs — middlewares, inline middlewares, route groups and subrouter mounting
  • No external dependencies — plain ol’ Go 1.7+ stdlib + net/http
  • Robust — in production at Pressly, CloudFlare, Heroku, 99Designs, and many others
  • Lightweight — cloc’d in ~1000 LOC for the chi router
  • And yes, it’s really fast.

My favorite being that the same old http handlers and middlewares you would write for other net/http compatible routers would work here as well.

Lets get started

First we create our main.go. The corner stone (or center piece 🙂) of our program.

Hightlights of some best practices in the code above

  1. Group routes into logical groups in individual packages, and then mount those routes:
r.Mount("/api/todo", todo.Routes())

2. Version API’s, so you can make api updates without breaking old clients:

router.Route("/v1", ....)

3. Use middlewares extensively. Most code that is heavily used by multiple routes, can be turned into chainable middlewares. eg authentication, setting response headers, compression, request logging, rate limiting, etc.

EDIT (I’ll throw more light on the walk function based on a question by Ajinkya in the comments.):

The chi router has a method called walk. The method recieves:

  • A router
  • A callback.

The callback is called for each route which was defined on the router, and recieves 4 parameters:

  1. The method defined for the route
  2. The actual route string
  3. The handler(function) which processes the request for the given route
  4. The list of middlewares defined for the given route (Middlewares are simply functions which get called before the handler is called, so they are used for preprocessing requests, authentication, etc)

In my example, i simply walk the router and print all the defined routes. This helps me see all my available routes at a glance.

Next we build the todo package, which actually holds our todo logic.

Things to note

  1. The todo package has a function which returns all it’s routes. The’s routes are mounted in main.go. In practice I would usually add these routes in file called routes.go, so it’s easy to find within the package.
  2. The handlers have the function signature of func (w http.ResponseWriter,r *http.Request) which means the handler you will be writing are no different form if you were using net/http in the standard library.
  3. The use of render.JSON which is just a wrapper around encoding/json which automatically escapes all html in your JSON response, and sets the content type to application/json

Are you shocked by how simple it is? Check out this project on github at https://github.com/tonyalaribe/todoapi/tree/master/basestructure.

In our the next article in this series, we will go ahead to add support for configuration, and shared states. Most projects will usually need external configuration like database connection details, etc. We will discuss this in the next article.

Don’t forget to say hi, or even let me know what you will like to learn. I just might be inspired to write about it 🙂.

Don’t forget to clap too, if this was useful to you.

PS:
I have a developer tool startup that tries to be smart and make it easier for us to manage the APIs and Backends we build. Detect issues, etc, automatically. If you’re building APIs, please try it out and give me feedback. I would really appreciate the feedback. This is the website:
apitoolkit.io

--

--

I help solve business challenges using technology. Golang and Web optimisation (PWA, service workers, progressive enhancement)