Book Image

Building RESTful Web services with Go

By : Naren Yellavula
Book Image

Building RESTful Web services with Go

By: Naren Yellavula

Overview of this book

REST is an architectural style that tackles the challenges of building scalable web services and in today's connected world, APIs have taken a central role on the web. APIs provide the fabric through which systems interact, and REST has become synonymous with APIs. The depth, breadth, and ease of use of Go, makes it a breeze for developers to work with it to build robust Web APIs. This book takes you through the design of RESTful web services and leverages a framework like Gin to implement these services. The book starts with a brief introduction to REST API development and how it transformed the modern web. You will learn how to handle routing and authentication of web services along with working with middleware for internal service. The book explains how to use Go frameworks to build RESTful web services and work with MongoDB to create REST API. You will learn how to integrate Postgres SQL and JSON with a Go web service and build a client library in Go for consuming REST API. You will learn how to scale APIs using the microservice architecture and deploy the REST APIs using Nginx as a proxy server. Finally you will learn how to metricize a REST API using an API Gateway. By the end of the book you will be proficient in building RESTful APIs in Go.
Table of Contents (20 chapters)
Title Page
Credits
About the Author
Acknowledgments
About the Reviewer
www.PacktPub.com
Customer Feedback
Preface

Building our first service – finding the Roman numeral


With the concepts we have built upto now, let us write our first basic REST service. This service takes the number range (1-10) from the client and returns its Roman string. Very primitive, but better than Hello World.

Design:

Our REST API should take an integer number from the client and serve back the Roman equivalent.

The block of the API design document may look like this:

HTTP Verb

PATH

Action

Resource

GET

/roman_number/2

show

roman_number

 

Implementation:

Now we are going to implement the preceding simple API step-by-step.

Note

Code for this project is available at https://github.com/narenaryan/gorestful

As we previously discussed, you should set the GOPATH first. Let us assume the GOPATH is /home/naren/go. Create a directory called romanserver in the following path. Replace narenaryan with your GitHub username (this is just a namespace for the code belonging to different users):

mkdir -p $GOPATH/src/github.com/narenaryan/romanserver

Our project is ready. We don't have any database configured yet. Create an empty file called main.go:

touch $GOPATH/src/github.com/narenaryan/romanserver/main.go

Our main logic for the API server goes into this file. For now, we can create a data file which works as a data service for our main program. Create one more directory for packaging the Roman numeral data: 

mkdir $GOPATH/src/github.com/narenaryan/romanNumerals

Now, create an empty file called data.go in the romanNumerals directory.  The src directory structure so far looks like this:

 ;

Now let us start adding code to the files. Create data for the Roman numerals:

// data.go
package romanNumerals

var Numerals = map[int]string{
  10: "X",
  9: "IX",
  8: "VIII",
  7: "VII",
  6: "VI",
  5: "V",
  4: "IV",
  3: "III",
  2: "II",
  1: "I",
}

We are creating a map called Numerals. This map holds information for converting a given integer to its Roman equivalent. We are going to import this variable into our main program to serve the request from the client. 

Open main.go and add the following code:

// main.go
package main

import (
   "fmt"
   "github.com/narenaryan/romanNumerals"
   "html"
   "net/http"
   "strconv"
   "strings"
   "time"
)

func main() {
   // http package has methods for dealing with requests
   http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
       urlPathElements := strings.Split(r.URL.Path, "/")
       // If request is GET with correct syntax
       if urlPathElements[1] == "roman_number" {
           number, _ := strconv.Atoi(strings.TrimSpace(urlPathElements[2]))
           if number == 0 || number > 10 {
           // If resource is not in the list, send Not Found status
               w.WriteHeader(http.StatusNotFound)
               w.Write([]byte("404 - Not Found"))
           } else {
             fmt.Fprintf(w, "%q", html.EscapeString(romanNumerals.Numerals[number]))
           }
       } else {
           // For all other requests, tell that Client sent a bad request
           w.WriteHeader(http.StatusBadRequest)
           w.Write([]byte("400 - Bad request"))
       }
   })
 // Create a server and run it on 8000 port
   s := &http.Server{
     Addr: ":8000",
     ReadTimeout: 10 * time.Second,
     WriteTimeout: 10 * time.Second,
     MaxHeaderBytes: 1 << 20,
   }
   s.ListenAndServe()
}

Note

Always use the Go fmt tool to format your Go code. Usage example: go fmt github.com/narenaryan/romanserver

Now, install this project with the Go command install:

go install github.com/narenaryan/romanserver

This step does two things:

  • Compiles the package romanNumerals and places a copy in the $GOPATH/pkg directory
  • Places a binary in the $GOPATH/bin

We can run the preceding API server as this:

$GOPATH/bin/romanserver

The server is up and running on http://localhost:8000. Now we can make a GET request to the API using a client like Browser or the CURL command. Let us fire a CURL command with a proper API GET request.

Request one is as follows:

curl -X GET "http://localhost:8000/roman_number/5" # Valid request

The response is as follows:

HTTP/1.1 200 OK
Date: Sun, 07 May 2017 11:24:32 GMT
Content-Length: 3
Content-Type: text/plain; charset=utf-8

"V"

Let us try a few incorrectly formed requests.

Request two is as follows:

curl -X GET "http://localhost:8000/roman_number/12" # Resource out of range

The response is as follows: 

HTTP/1.1 404 Not Found
Date: Sun, 07 May 2017 11:22:38 GMT
Content-Length: 15
Content-Type: text/plain; charset=utf-8

404 - Not Found

Request three is as follows:

curl -X GET "http://localhost:8000/random_resource/3" # Invalid resource

The response is as follows:

"HTTP/1.1 400 Bad request
Date: Sun, 07 May 2017 11:22:38 GMT
Content-Length: 15
Content-Type: text/plain; charset=utf-8
400 - Bad request

Our little Roman numerals API is doing the right thing. The right status codes are being returned. That is the point all API developers should keep in mind. The client should be informed why something went wrong.

Breaking down the code

We just updated the empty files in one single go and started running the server. Let me now explain each and every piece of the file main.go:

  • Imported a few packages. github.com/narenaryan/romanNumerals is the data service we created before.
  • net/http is the core package we used to handle an HTTP request through its HandleFunc function. That function's arguments are http.Request and http.ResponseWriter. Those two deal with the request and response of an HTTP request.
  • r.URL.Path is the URL path of the HTTP request. For the CURL Request one, it is /roman_number/5. We are splitting this path and using the second argument as a resource and the third argument as a value to get the Roman numeral. The Split function is in a core package called strings.
  • The Atoi function converts an alphanumeric string to an integer. For the numerals map to consume, we need to convert the integer string to an integer. The Atoi function comes from a core package called strconv.
  •  We use http.StatusXXX to set the status code of the response header. The WriteHeader and Write functions are available on the response object for writing the header and body, respectively.
  • Next, we created an HTTP server using &http while initializing a few parameters like address, port, timeout, and so on.
  • The time package is used to define seconds in the program. It says, after 10 seconds of inactivity, automatically return a 408 request timeout back to the client.
  • EscapeString escapes special characters to become valid HTML characters. For example, Fran & Freddie's becomes Fran &amp; Freddie's&#34.
  • Finally, start the server with the  ListenAndServe function. It keeps your web server running until you kill it.

Note

One should write unit tests for their API. In the upcoming chapters, we will see how to test an API end to end. 

Live reloading the application with supervisord and Gulp

Gulp is a nice tool for creating workflows. A workflow is a step-by-step process. It is nothing but a task streamlining application. You need NPM and Node installed on your machine. We use Gulp to watch the files and then update the binary and restart the API server. Sounds cool, right?

The supervisor is an application to reload your server whenever the application gets killed. A process ID will be assigned to your server. To restart the app properly, we need to kill the existing instances and restart the application. We can write one such program in Go. But in order to not reinvent the wheel, we are using a popular program called supervisord.  

Monitoring your Go web server with supervisord

Sometimes your web application may stop due to operating system restarts or crashes. Whenever your web server gets killed, it is supervisor's job to bring it back to life. Even the system restart cannot take your web server away from the customers. So, strictly use supervisord for your app monitoring.

Installing supervisord

We can easily install supervisord on Ubuntu 16.04, with the apt-get command:

sudo apt-get install -y supervisor

This installs two tools, supervisor and supervisorctl. supervisorctl is intended to control the supervisord and add tasks, restart tasks, and so on.

On macOS X, we can install supervisor using the brew command:

brew install supervisor

Now, create a configuration file at:

/etc/supervisor/conf.d/goproject.conf

You can add any number of configuration files, and supervisord treats them as separate processes to run. Add the following content to the preceding file:

[supervisord]
logfile = /tmp/supervisord.log
[program:myserver]
command=$GOPATH/bin/romanserver
autostart=true
autorestart=true
redirect_stderr=true

By default, we have a file called .supervisord.conf at /etc/supervisor/. Look at it for more reference. In macOS X, the same file will be located at /usr/local/etc/supervisord.ini.

Coming to the preceding configuration:

  • The [supervisord] section tells the location of the log file for supervisord
  • [program:myserver] is the task block which traverses to the given directory and executes the command given

Now we can ask our supervisorctl to re-read the configuration and restart the tasks (process). For that, just say:

  • supervisorctl reread
  • supervisorctl update

Then, launch supervisorctl with the command:

supervisorctl

You will see something like this:

Note

supervisorctl is a great tool for controlling supervisor programs.

   

Since we named our romanserver myserver in the supervisor configuration file, we can start, stop, and restart that program from supervisorctl.