DEV Community

Cover image for Attempting to Learn Go - Consuming a REST API
Steve Layton
Steve Layton

Posted on • Updated on

Attempting to Learn Go - Consuming a REST API

I've been thinking a lot about REST APIs recently. Working for a SaaS company seems to all but guarantees that you will come in contact with one (or dozens) so I suppose that makes sense. Since I've been working on some basic code and practicing my Go, I thought that it might be nice to jot down some notes and maybe some code to continue my Learning Go "series".

Getting started with basic API consumption is pretty easy thanks to Go's built-in net/http package. Practically everything needed to query a remote API is available in the Go standard library.

We'll start with our standard Go opening... and sticking with main for our package name since this is just an example. We'll import fmt, io/ioutil, net/http - strictly speaking we don't need fmt but since we're not really doing anything with the API response, other than printing it out it's included.

package main

import (
    "fmt"
    "io/ioutil"
    "net/http"
)

Next let's open our main function and declare our API URL, for this exercise we're just going to query httpbin.org. Passing our URL and the "GET" method to http.NewRequest will give us back a proper http.Request object (if no error occurs) that we can use in the next step.

func main() {
    APIURL := "https://httpbin.org/get"
    req, err := http.NewRequest(http.MethodGet, APIURL, nil)
    if err != nil {
        panic(err)
    }

Using http we create a default client, this will handle the actual request to the remote server. Finally, we'll call Do() and actually make the request, receiving back a response or an error. It's Go so we'll check for errors and move on if we don't have one.

    client := http.DefaultClient
    resp, err := client.Do(req)
    if err != nil {
        panic(err)
    }

Assuming we received a valid response we're going to begin to process it. First, we'll defer closing the response body, this way we can read out the data and Go will simply take care of closing it once the surrounding function returns. But first, we'll use ioutil.ReadAll to read in the body and return an array of bytes - as long as there is no error that is.

    defer resp.Body.Close()
    body, err := ioutil.ReadAll(resp.Body)
    if err != nil {
        panic(err)
    }

Finally, lets convert our array of bytes to a string and print it to standard out.

    fmt.Printf("%v", string(body))

And presto! That's all there is to get data from a remote REST endpoint in Go.

{
  "args": {},
  "headers": {
    "Accept-Encoding": "gzip",
    "Connection": "close",
    "Host": "httpbin.org",
    "User-Agent": "Go-http-client/1.1"
  },
  "origin": "68.211.xx.xx",
  "url": "https://httpbin.org/get"
}

Next time I think I'll expand this into a module that we can use for future REST work. From there we'll probably write something that actually makes use of the consumed API.


You can find the code for this and most of the other Attempting to Learn Go posts in the repo on GitHub.



Oldest comments (5)

Collapse
 
bgadrian profile image
Adrian B.G. • Edited

Nice!

Just to be picky, when I see Go code I can't help it sorry, ALL_UPPER is not idiomatic, even constants follow the normal naming conventions camel/pascal case (APIURL).

And a warning for the new gopher readers, be careful in production:

  • the default net/http instances does not timeout, including the default http.Defaultclient
  • you usually do not use ioutil.ReadAll, especially when you read the Requests (as a web server), if someone post you a 3GB payload it will try to read it all. To protect our code we can use Limiters like golang.org/pkg/net/http/#MaxBytesR...

Having a public web server inside our code is one of the biggest challenge, especially for the devs coming from an interpreted languge, at least for me. We do not have all the protection Nginx or Apache gave us, we have to be more careful. Using Throttlers, Limiters, Timeouts and such is a must!

Also when speaking about REST, I recommend using goswagger.io/, it handles everything for us, generating Server and Client code based on a Swagger config.

Collapse
 
shindakun profile image
Steve Layton

Thanks for the comments! I usually just defer to golint and try not think about it too much, the machine (or the authors of golint in this case) prefers all uppercase. main.go:12:6: var ApiURL should be APIURL. Yeah for a production webserver we'd definitly want to set up a timeout, typically I'd tend to deply behind Nginx anyway to take advantage of its caching. This example will probably change a bit over the next couple posts but it will likely not turn into a webserver (at least not yet) so I think ioutil.ReadAll and the http.DefaultClient should be fine for now. Though now you've given me another post I could mix in. ;)

Collapse
 
ohhnate profile image
Samuel Fuller

I had been trying to have my go program communicate with an API for like 2 days now.. Once I came across this post I was able to get everything working as intended. Very basic, straightforward, and life saving. Thank you so much.

Collapse
 
shindakun profile image
Steve Layton

Nice! Thanks for letting me know it helped!

Collapse
 
stampingi profile image
Stamping.io

I have this problem:

panic: Get httpbin.org/get: dial tcp: lookup httpbin.org on [::1]:53: read udp [::1]:45938->[::1]:53: read: connection refused