Search icon CANCEL
Arrow left icon
Explore Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Conferences
Free Learning
Arrow right icon
Arrow up icon
GO TO TOP
Building RESTful Web services with Go

You're reading from   Building RESTful Web services with Go Learn how to build powerful RESTful APIs with Golang that scale gracefully

Arrow left icon
Product type Paperback
Published in Dec 2017
Publisher Packt
ISBN-13 9781788294287
Length 316 pages
Edition 1st Edition
Languages
Tools
Arrow right icon
Author (1):
Arrow left icon
Naren Yellavula Naren Yellavula
Author Profile Icon Naren Yellavula
Naren Yellavula
Arrow right icon
View More author details
Toc

Table of Contents (13) Chapters Close

Preface 1. Getting Started with REST API Development FREE CHAPTER 2. Handling Routing for Our REST Services 3. Working with Middleware and RPC 4. Simplifying RESTful Services with Popular Go Frameworks 5. Working with MongoDB and Go to Create REST APIs 6. Working with Protocol Buffers and GRPC 7. Working with PostgreSQL, JSON, and Go 8. Building a REST API Client in Go and Unit Testing 9. Scaling Our REST API Using Microservices 10. Deploying Our REST services 11. Using an API Gateway to Monitor and Metricize REST API 12. Handling Authentication for Our REST Services

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.

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()
}
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.
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:

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.

You have been reading a chapter from
Building RESTful Web services with Go
Published in: Dec 2017
Publisher: Packt
ISBN-13: 9781788294287
Register for a free Packt account to unlock a world of extra content!
A free Packt account unlocks extra newsletters, articles, discounted offers, and much more. Start advancing your knowledge today.
Unlock this book and the full library FREE for 7 days
Get unlimited access to 7000+ expert-authored eBooks and videos courses covering every tech area you can think of
Renews at €18.99/month. Cancel anytime