Writing a client

APIs are built so that we can write applications on top of our service. APIs allow us to write clients that access our service apart from the browser.

 1 package main
 2 
 3 import "net/http"
 4 import "fmt"
 5 import "io/ioutil"
 6 
 7 func main() {
 8 resp, err := http.Get("http://127.0.0.1:8081/")
 9 if err != nil {
10     fmt.Println(err)
11 }
12 
13 defer resp.Body.Close()
14 body, err := ioutil.ReadAll(resp.Body)
15 if err != nil {
16     fmt.Println("Error reading body")
17 }
18 fmt.Println(string(body))
19 }

When we use the service via a browser, the browser is essentially making the GET and POST calls on our behalf. When we have to write an app to access our service, we have to write those methods in the app.

For executing the above code, we need to ensure that our Tasks application is running on port 8081. When we execute, it must print the login html page on the terminal.

This might be a great example to get started with using HTTP methods from Go, but for our application we require to send POST reqests with content type as form and send the form data as request body.

Getting the token

We use the PostForm method which will send a Form via a POST method, effectively saving the trouble of setting the content type field for us. If you run the below code, it will print the authentication token on the terminal.

 1 import "net/http"
 2 import "net/url"
 3 import "fmt"
 4 import "io/ioutil"
 5 
 6 func main() {
 7     usernamePwd := url.Values{}
 8     usernamePwd.Set("username", "suraj")
 9     usernamePwd.Set("password", "suraj")
10 
11     resp, err := http.PostForm("http://127.0.0.1:8081/api/get-token/", usernamePwd)
12     if err != nil {
13         fmt.Println(err)
14     }
15 
16     defer resp.Body.Close()
17     body, err := ioutil.ReadAll(resp.Body)
18     if err != nil {
19         fmt.Println("Error reading body")
20     }
21     fmt.Println(string(body))
22 }

The next steps are to store this token and get the task list.

Error handling

We should throw a user friendly error message if the Tasks application isn’t running in the background. And then exit the client.

Example

The below code will get a new token and print all the tasks for that particular user.

 1 package main
 2 
 3 import (
 4     "fmt"
 5     "io/ioutil"
 6     "net/http"
 7     "net/url"
 8     "os"
 9 )
10 
11 func main() {
12     baseURL := "http://127.0.0.1:8081/"
13 
14     usernamePwd := url.Values{}
15     usernamePwd.Set("username", "suraj")
16     usernamePwd.Set("password", "suraj")
17 
18     resp, err := http.PostForm(baseURL+"api/get-token/", usernamePwd)
19     if err != nil {
20         fmt.Println("Is the server running?")
21         os.Exit(1)
22     } else {
23         fmt.Println("response received")
24     }
25 
26     defer resp.Body.Close()
27     body, err := ioutil.ReadAll(resp.Body)
28     if err != nil {
29         fmt.Println("Error reading body")
30     } else {
31         fmt.Println("Token received")
32     }
33     token := string(body)
34 
35     client := &http.Client{}
36     req, err := http.NewRequest("GET", baseURL+"api/get-task/", nil)
37 
38     if err != nil {
39         fmt.Println("Unable to form a GET /api/get-task/")
40     }
41 
42     req.Header.Add("Token", token)
43     resp, err = client.Do(req)
44 
45     if (err != nil) || (resp.StatusCode != 200) {
46         fmt.Println("Something went wrong in the getting a response")
47     }
48 
49     defer resp.Body.Close()
50     body, err = ioutil.ReadAll(resp.Body)
51     fmt.Println(string(body))
52 
53 }

Advanced Usage

We can write a complete command line client for tasks in Go as we saw in this chapter, it would mean implementing a lot of command line flags to get what the user wants us to get and we would want to store the key in a text file to cache it.

Homework

  • Read the unit testing chapter and write a unit test to test the API client rather than using the firefox addon.
  • Build a REST api client to Tasks and add/delete notes via the command line. Note that you’ll have to modify how things look & not generate HTML. Render just the markdown text.

-Previous section -Next section