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.
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.