Writing a Go client for your RESTful API

(This article was originally posted on Medium)

If you are providing a RESTful API for your product, you are already doing developers around the world a huge favor by enabling them to do amazing things, things that you may not even had imagined were possible. By providing your users with a client SDK in their favorite language you are making it even easier to integrate their services with yours. In this blog post I will show you how to write your own client package in Go!

The example we will be using throughout this post is a generic chat service — we will call it The Great Chatsby — which resembles a chat service you might already be using in your organization. The Great Chatsby provides a RESTful API that allows us to create channels and send chat messages to them. It also supports listing messages sent to a channel as well as all the registered users. For the remainder of this post we will write a client package, github.com/thegreatchatsby/chatsby-go, so that developers can write integrations with their products.

Your first Go client SDK

First off, you are going to need a Client struct to hold information about where to find the API you are going to consume. It will also contain a reference to the http.Client used to make request to our API.

Pro tip: By making the base URL configurable we can make it testable by passing the URL of a httptest.Server.

Next, let us create a method for listing users. We create a complete URL from our base URL and relative path, and use it to build a request that we can send using our http.Client.

Note: Keep in mind that the Transport is only able to reuse the HTTP connection if the response body has been drained completely. We can ensure this is the case by calling io.Copy(ioutil.Discard, resp.Body) right before resp.Body.Close is called. Check out this thread to learn more.

Edit: As pointed out by some, in practice you probably want to use io.CopyN in order to not drain an unbounded number of bytes. Note though that draining the body is really only necessary if you can realistically expect junk data to follow your response.

When we implement the rest of the API operations however, we see that there are some things that we can extract to their own methods. By extracting our own newRequest() and do() methods, we can reuse them for all our API operations:

You can even provide Go methods for each HTTP method as a further convenience. Depending on how consistent your requests and responses are, you can conveniently encode/decode them in the newRequest() and do()methods, or where you do the actual request. When we are done, the complete API will look something like this:

Often, a Client with a bunch of methods is all you are going to need, especially if the REST API is fairly small. Both docker/docker and nlopes/slack are examples of two decent-sized API clients that are taking this pattern to the limit. However, when the API grows larger and we start exposing several services, you might want to consider using service objects.

Service objects

At some point, you are going to want to break up your package into multiple files, e.g. client.go, chat.go, channels.go, and users.go. docker/docker deals with this by communicating services in the filename, e.g. container_list.go, and by prefixing each method with the service name, e.g. client.ContainerList() (check out their Client object on godoc.org).

If you do not like spreading methods across multiple files, another approach is to break down your API into service objects that group related operations. Each service object would then contain a reference to the Client. This approach is used by for example google/go-github.

A common practice is to have service objects as fields in the Client. When creating the client, you can then pass a reference to the Client to each service object in the NewClient function:

Configuring the http.Client

Depending on how customizable you want the API to be, you can go for simply passing an http.Client, or you could go for the functional optionsapproach, letting your users pretty much customize all they want. For example, if you provide a library for a hosted service you might rely on a defaultEndpoint, while if you are talking to an on-site installation you might provide a func BaseURL(u string) func(*Client) option.

Regardless of the approach you go with, you should provide a means of letting users configure their own http.Client, e.g. setting sensible timeout values. This also allows you to handle authentication outside your Client. Here is an example from google/go-github of how you can authenticate using OAuth2 access tokens.

Pro tip: If you have several ways of letting users authenticate themselves, consider providing helper functions for creating authenticated http.Clients.

Keeping a reference to a http.Client lets us handle authentication, timeouts and other things independently from your API. Allowing your users to pass their own HTTP client used by your library lets them configure settings that make sense in their context.

Tips and tricks

Dealing with errors

Arguably the simplest way to deal with errors coming from you API is to simply check whether the status code in the response is anywhere between 200≤ and <400, or otherwise return an error.

Depending on how your RESTful API returns errors, consider converting them into proper Go errors instead of generic API errors, e.g. check for 404 and instead return a instance of ErrChannelNotFound. Also make sure to document that ErrChannelNotFound is returned when channel is not found. This makes for a more pleasant and consistent experience for your users.

Pointer fields

To distinguish between unset fields and zero-value fields, structs can use pointer values.

This can be helpful but it can also make for a rather unsightly code when using it. In this case make sure to provide helper functions for creating pointers to common types.

This is obviously more tedious than using non-pointer values so do this only if it makes sense for your API. Check out google/go-github and aws/aws-sdk-go to see this in action. Compare with tambet/go-asana where values are just as effective.

Summary

Go makes it easy writing production-ready client packages that allow your users to consume your RESTful services safely and efficiently from their Go applications. In this blog post you have seen how you can get started writing your own. If you have experience writing client SDKs in Go and you feel I left something out, please let me know by commenting on this post!

Post mortem: Cachningsproblem störde TV4 Play

TV4 Play hade problem torsdag-fredag – det här var vad som hände:

Som vi tidigare berättat bygger alla kanalen som har TV4 Play på ett och samma API. Slutar API:t fungera slutar också de olika kanalerna (webb, mobil, tablet, OTT, Plex m.fl.) fungera. Torsdag kväll får vi larm som tyder på att API:t fungerar dåligt. Felsökning börjar direkt. Graferna visar att klockan 21.00 händer något som gör API:t långsamt:

Responstider från API:t (från webb-versionen av TV4Play).

Eftersom vi inte gjort någon förändring eller deploy vid tillfället är det sannolikt att innehållet i tjänsten på något sätt påverkat (eftersom det sker så tydligt klockan 21.00). Vi kollar vad som publicerats och ser att det finns några program som publicerats både före, vid och efter 21.00 men det ser inte ut att vara något konstigt med det och alla program fungerar att titta på.

När vi lägger oss torsdag kväll fungerar sajten ok men vi har stängt av mobil- och tabletversionerna: med de igång dyker API:t direkt. På morgonen fortsätter vi undersöka vad som hänt och försöker ringa in var, varför och hur problemet uppstått. Det mesta ser ut att fungera precis som det ska men trots det är API:t exremt långsamt och trögt. Bingo blir det när vi hittar en fil som är ocachad. Filen – som i XML beskriver hela innehållsstrukturen för TV4 Play – levereras ocachad. Eftersom den används frekvent sänker det nästan allt.

Efter ett tag går det också att klura ut vad som hänt: för att få API:t snabbt använder vi Memcached för caching av innehåll. Memcached har en default-gräns för hur stora saker som ska cachas satt till 1 MB. Under hela torsdagen satt redaktionen och fyllde på mer innehåll i TV4 Play. Fler moduler och kategorier gjorde att kategoriträdet – och därmed XML-filen – växte. Vad vi inte reflekterat över är att memcached har gränsvärdet för hur stora objekt som cachas och att vi hela tiden närmar oss den.

Klockan 21.00 sker så en tidsinställd publicering av en ny serie “Livet på flygplatsen” (ett program och en ny kategori publiceras). Det är den lilla information som får XML-filen att växa över memcacheds gränsvärde. Det i sin tur leder till att filen slutar cachas och hämtas dynamiskt vilket leder till att API:t och därmed alla kanaler med TV4 Play blir långsamma.

Fixen till det här problemet var inte ens en rad kod (=> komprimera xml-filen) vilket minskade filstorleken ungefär 80%. Inom kort kommer vi också förbättra hur XML-filen hanteras för att få en bestående lösning.

Vi beklagar det inträffade. Förutom de kodningsmässiga åtgärderna har vi även satt in några extra övervakningslarm för att undvika liknande problem igen.

Totalt var det 15 timmar med dålig tillgänglighet.

Till sist: här är programmet som publicerades (Premium). Hur många videos som finns i TV4Play? 108.703 and counting…

Apropå interface

I ett inlägg om myndigheter ska släppa data fritt lanserade Tomas Wennström i sin presentation uttydningen “ap-enkelt interface” för förkortningen API. Myndigheter, företag och organisationer bör alla fundera vad deras kärnverksamhet egentligen är och vad som gagnar den bäst. Det är ingen hemlighet att TV4-Gruppen har som mål att få intäkter från reklam och betal-tv-avgifter. Därför är det inget främmande att man ska kunna ta del av TV4:s program även på andra sätt än i TV-kanalerna och på våra egna sajter, så länge vi antingen får visa reklam eller ta betalt av tittarna. Idag kan man t.ex. se TV4 Play i digital-TV-operatörernas boxar och nästa TV du köper kommer antagligen komma med TV4 Play inbyggt.

Integrationen mot andra tjänsteleverantörer har hittills varierat från partner till partner vilket gjort att det varit ganska kostsamt att integrera. När vi skulle utveckla nya versionen av TV4 Play och det blev aktuellt att göra saker ordentligt föddes tanken på att dela upp tjänsten i två delar: de befintliga systemen för hantering av videor och redaktionell hantering av metadata behölls men till skillnad mot tidigare så blev kopplingen till presentationslagret lösare. Från att tidigare använt Polopoly för redigering och lagring av metadata såväl som presentation av webbplatsen så ligger nu presentationen löst i en egen webbapp som hämtar informationen från ett API. Men inte bara webbappen utan även iPhone-, iPad- och Android-appar använder samma API. Uppkommer ett behov av viss data för någon så är det stor chans att det är till nytta även för andra appar och man har sparat tid och kraft.

Tanken har hela tiden varit att öppna upp detta API. Än är vi dock i en situation där API:t ändrats lite väl ofta och varit lite väl instabilt för att våga släppa information. För har man väl en gång lanserat en funktion i ett API så är det svårt att göra ändringar eller ta bort den utan erbjuda alternativa sätt att åstadkomma samma sak eller åtminstone ha varslat långt innan. Med tanke på att vi fortfarande arbetar febrilt med nyutveckling på de centrala delarna i plattformarna så ligger ett släpp fortfarande några månader bort.

Ett smakprov på vad man kan göra av API:t kan jag dock ge. Med lite PHP-kod, vårt API (kallat API4) och Facebooks gamla API har jag skapat flikar med videoklipp för en rad olika fanpages för TV4-program. Senast i raden var den för Sportens Kanon och kalkon (att senaste veckans inslag saknas visar på vikten av att ha ordentligt metadata i tjänsten, i det här fallet så finns det senaste klippet i Play men ligger under en annan kategori än den väntade).

Ett annat exempel på användning av API:t är en WordPress-plugin som är en vidareutveckling på Bambuser-postningspluginen som istället för att bevaka en Bambuser-kanal håller koll på en sökning i API4 och automatpublicerar ett inlägg. På min privata blogg publiceras automatiskt ett nytt inlägg när det kommer en video som innehåller namnet “Jardenberg”. Eftersom Joakim Jardenberg dyker upp som expert i Nyhetsmorgon emellanåt var det bara en tidsfråga innan det skulle hända. För en månad sedan hände det, Joakim deltog i en diskussion med anledning av Wikileaks och vips var inlägget där med inbäddat videoklipp.