New Luno API SDKs

From code to client
Many customers interact with Luno using our API, which gives anyone access to market data. Developers use the Luno API to build applications to automatically buy, sell, send, receive, deposit and withdraw supported local and cryptocurrencies.
If this still makes sense to you, read on.
The API documentation lists SDKs for popular programming languages. Only the Go SDK, though, is officially supported.
Maintaining SDKs in multiple languages is difficult and costly. Each supported language requires duplicated work in another language, leading to bugs and constant maintenance.
To automate this process, we’ve annotated all our API handlers in our internal Go code with Swagger. The swagger
tool can read annotations and generate an OpenAPI spec. The generated spec is a JSON file describing each annotated endpoint, its input parameters and its output.
// swagger:parameters getOrderBook
type getOrderbookRequest struct {
// in: query
// required: true
Pair string `json:"pair"`
}
// swagger:model
type getOrderBookResponse struct {
Bids []Order `json:"bids"`
Asks []Order `json:"asks"`
}
// swagger:route GET /api/1/orderbook orders getOrderBook
//
// Get order book
//
// Returns a list of bids and asks in the order book. Ask orders are sorted
// by price ascending. Bid orders are sorted by price descending. Note that
// multiple orders at the same price are not necessarily conflated.
//
// Responses:
// 200: body:getOrderBookResponse description:OK
func GetOrderBook(w http.ResponseWriter, r *http.Request) {
// ...
}
getOrderBook
, requiring a query-string parameter called pair
. It also designates that the endpoint’s response, if the HTTP response code is 200, will be a JSON message with a list of bid and ask orders.
To generate an OpenAPI JSON spec for the above code, you would install the swagger
tool (go get -u github.com/go-swagger/go-swagger
) and run swagger generate spec
.
"/api/1/orderbook": {
"get": {
"description": "Returns a list of bids and asks in the order book. Ask orders are sorted by\nprice ascending. Bid orders are sorted by price descending. Note that\nmultiple orders at the same price are not necessarily conflated.",
"tags": [
"market"
],
"summary": "Get order book",
"operationId": "getOrderBook",
"parameters": [
{
"type": "string",
"format": "pair",
"example": "XBTZAR",
"description": "Currency pair",
"name": "pair",
"in": "query",
"required": true
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/getOrderBookResponse"
}
}
}
}
}
type GetOrderBookRequest struct {
// Currency pair
Pair string `json:"pair" url:"pair"`
}
type GetOrderBookResponse struct {
Asks []OrderBookEntry `json:"asks"`
Bids []OrderBookEntry `json:"bids"`
Timestamp int64 `json:"timestamp"`
}
// GetOrderBook makes a call to GET /api/1/orderbook.
//
// Returns a list of bids and asks in the order book. Ask orders are sorted by
// price ascending. Bid orders are sorted by price descending. Note that
// multiple orders at the same price are not necessarily conflated.
func (cl *Client) GetOrderBook(ctx context.Context, req *GetOrderBookRequest) (*GetOrderBookResponse, error) {
var res GetOrderBookResponse
err := cl.do(ctx, "GET", "/api/1/orderbook", req, &res, false)
if err != nil {
return nil, err
}
return &res, nil
}
getOrderBook
endpointswagger generate client
. But we decided to write our own set of tools to do this. We wanted the code to be as lightweight as possible, and to be able to tweak the treatment of data structures. The core client generation code essentially traverses the JSON spec and invokes Go templates to output the individual functions.
// {{.Name}} makes a call to {{.Method}} {{.Path}}.
//
{{range .Description -}}
// {{.}}
{{end -}}
func (cl *Client) {{.Name}}(ctx context.Context, req *{{.Request.Name}}) (*{{(index .Responses 0).Name}}, error) {
// …
}
As a start, we’ve made the generated Go SDK available. Alpha releases for PHP and Python will follow shortly.
Did you find this interesting? Help us take this further, join our engineering squad.