Learn the DNSimple API by Bulding a CLI in Golang

Next week is dotGo 2016, in Paris, France. dotGo is Europe's largest Go conference, bringing together more than 600 developers to hear about the latest and greatest that the Golang community has to offer. In preparation, I'd like to give you a walk through of the basics of the official DNSimple API Go client, providing details on how to connect to the DNSimple API and perform various tasks to automate your domain management from your Go applications.

Getting Started

First, you'll need to have Go installed and set up correctly on your system. Once you've done that, you'll need to create a project in your $GOPATH. For example, I am going to start a project in the directory from my personal GitHub account: cd $GOPATH/src/github.com/aeden && mkdir dnsimple-example && cd dnsimple-example. You will need to change the path to something that works for you.

Once you've done that, you need to install the DNSimple Go library: go get github.com/dnsimple/dnsimple-go/dnsimple.

Authenticating

The simplest way to authenticate with the DNSimple API is to create an access token that you can use directly with your account. You may do that by following this guide.

Once you have your access token, you'll need to get it into the client. For this exercise, you will pass the token in as an environment variable, connect to the whoami endpoint and retreive some basic account information. Change the line for setting oauthToken to the following:

oauthToken:=os.Getenv("TOKEN")

Now when you run this code you'll need to also ensure that the token you created on the DNSimple site is passed in via the TOKEN environment variable. For example:

At this point you have connected to the DNSimple API and have been able to perform an authenticated call. Now you can move on to working with your domains.

Listing Domains

To list the domains in your account, you'll use the Domains.ListDomains method. It requires the account ID as its first argument, so you'll need to get that from the whoami response you previously printed. The ID in the account struct is an int type and the ListDomains method requires a string type, so you'll need to convert the type using Go's built-in strconv.Itoa function.

Here is what the section of code starting after the printing of the account data looks like:

Make sure to also add the import of the strconv package in the import section of the code.

If you run this code you will see that the first line outputs the account and the subsequent lines output information about each domain returned. Note that if you have more than 30 domains then this will only return the first page of domains.

Interlude: Switching to Command Functions

Before we move on, let's clean up the code a bit. First, instead of printing the entire Account struct, change the line to print something a bit simpler and clear: the email address from the account:

fmt.Printf("Connected as %+v\n\n",whoamiResponse.Data.Account.Email)

Next you can introduce a simple command mechanism so your program can be used to do different things. Each command will be implemented as a function. A builder function is used to return the specific command function. For example, here is the function that builds the list domains function:

The client is passed in when the function is built and the function returns the anonymous function that implements the command. That anonymous function will always expect an account ID (since we will always need the account ID).

The command name will be passed as the first argument to the program, so the main function needs to be updated to select the command or fallback to a usage message if no matching command is found. First, here's the usage function:

funcusage(commandNamestring){ifcommandName==""{fmt.Printf("Command is required\n\n")}else{fmt.Printf("Unknown command: %v\n\n",commandName)}fmt.Printf("The following commands are supported:\n")fmt.Printf(" domains.list\n")}

And now the updated main function:

funcmain(){oauthToken:=os.Getenv("TOKEN")client:=dnsimple.NewClient(dnsimple.NewOauthTokenCredentials(oauthToken))listDomains:=buildListDomainsFunc(client)commands:=map[string]func(string){"domains.list":listDomains,}iflen(os.Args)<=1{usage("")return}commandName:=os.Args[1]commandFunc:=commands[commandName]ifcommandFunc==nil{usage(commandName)}else{fmt.Printf("Connecting to DNSimple API...\n\n")whoamiResponse,err:=client.Identity.Whoami()iferr!=nil{fmt.Printf("Whoami() returned error: %v\n",err)os.Exit(1)}fmt.Printf("Connected as %+v\n\n",whoamiResponse.Data.Account.Email)accountId:=strconv.Itoa(whoamiResponse.Data.Account.ID)commandFunc(accountId)}}

The main function breaks down into the following steps:

Build the client

Create the command functions

Map the command names to their functions

Retrieve the command name from the command-line args

Retrieve the command function from the command map

Get the account ID

Execute the command function

To call the program now:

$ TOKEN=token go run myapp.go domains.list

Or, to display the help message:

$ TOKEN=token go run myapp.go

Or

$ TOKEN=token go run myapp.go bogus

Adding a Domain

Next you will implement a command to add a new domain. Since this new command requires at least one additional argument (the domain name), we will need to change the command implementation a bit as well.

First, let's consider how to have commands that can take a variety of arguments passed in when the applicaiton is run. I selected the following format:

domains.create name:example.com

Thus, the first argument is the name of the command and then subsequent arguments are name/value pairs.

Now the command function signature needs to be changed to accept both the account identifier and a map of options.

This function expects the domain name to be passed as domain and requires the type and content parameters. If you do not provide a value for name, then the record is created on the naked domain. Finally, if the TTL is present this function converts it to an int as that was it required by the dnsimple.ZoneRecord struct.

Take a look at this gist to see how the code should look at this point.

The Registrar.RegisterDomain function requires three arguments: the account ID, the domain name, and a RegistrarRequest struct. Inside the RegistrarRequest struct instance you must specify the registrant ID as an integer. Thus, this function converts the registrant ID from a string to an integer and sets the value in the registrarRequest before calling Registrar.RegisterDomain.

Conclusion

This blog post just scratches the surface of what you can do with the DNSimple API. The examples above are oriented towards building a CLI tool, but our client works great even if you want to integrate domain or record management into your application.