Book Image

Hands-On RESTful Web Services with Go - Second Edition

By : Naren Yellavula
Book Image

Hands-On RESTful Web Services with Go - Second Edition

By: Naren Yellavula

Overview of this book

Building RESTful web services can be tough as there are countless standards and ways to develop API. In modern architectures such as microservices, RESTful APIs are common in communication, making idiomatic and scalable API development crucial. This book covers basic through to advanced API development concepts and supporting tools. You’ll start with an introduction to REST API development before moving on to building the essential blocks for working with Go. You’ll explore routers, middleware, and available open source web development solutions in Go to create robust APIs, and understand the application and database layers to build RESTful web services. You’ll learn various data formats like protocol buffers and JSON, and understand how to serve them over HTTP and gRPC. After covering advanced topics such as asynchronous API design and GraphQL for building scalable web services, you’ll discover how microservices can benefit from REST. You’ll also explore packaging artifacts in the form of containers and understand how to set up an ideal deployment ecosystem for web services. Finally, you’ll cover the provisioning of infrastructure using infrastructure as code (IaC) and secure your REST API. By the end of the book, you’ll have intermediate knowledge of web service development and be able to apply the skills you’ve learned in a practical way.
Table of Contents (16 chapters)

ServeMux – a basic router in Go

ServeMux is an HTTP request multiplexer. The HandleFunc we used in the preceding section is actually a method of ServeMux. By using ServeMux, we can handle multiple routes. We can also create our own multiplexer. A multiplexer handles the logic of separating routes with a function called ServeHTTP. So, if we create a Go struct with the ServeHTTP method, it can do the job as the in-built multiplexer.

Consider a route as a key in a Go dictionary (map) and a multiplexer as its value. Go finds the multiplexer from the route and tries to execute the ServeHTTP function. In the following section, we will see the usage of ServeMux by creating an API that generates UUID strings.

Developing a UUID generation API using ServeMux

A UUID is a unique identifier for a resource or a transaction. UUIDs are widely used for identifying an HTTP request. Let us develop an API for generating a UUID. Please follow these steps:

  1. Create the program file as follows:
touch -p $GOPATH/src/github.com/git-user/chapter2/uuidGenerator/main.go
  1. Any Go struct with a few dedicated HTTP methods is qualified to be a ServeMux. For example, we can create a custom UUID struct and implement the ServeHTTP function in order to make it a ServeMux object. Following is the implementation for the uuidGenerator.go module:
import (
"crypto/rand"
"fmt"
)

// UUID is a custom multiplexer
type UUID struct {
}

func (p *UUID) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if r.URL.Path == "/" {
giveRandomUUID(w, r)
return
}
http.NotFound(w, r)
return
}

func giveRandomUUID(w http.ResponseWriter, r *http.Request) {
c := 10
b := make([]byte, c)
_, err := rand.Read(b)
if err != nil {
panic(err)
}
fmt.Fprintf(w, fmt.Sprintf("%x", b))
}

It consists of the UUID struct that acts as a ServeMux object. We can access the URL path in the handler function and use that information to manually route the requests to different response generators.

giveRandomUUID is a response generator function that sets a random UUID string to response. Go's crypto package has a Read function that fills random characters into a byte array.

  1. Now add a main function to the module using the ServeMux object. We should pass our ServeMux to the http.ListenAndServe function to get our content served. We are serving our content on port 8000:
package main

import (
"net/http"
)

func main() {
mux := &UUID{}
http.ListenAndServe(":8000", mux)
}

We use UUID as a multiplexer in the ListenAndServe function, which starts an HTTP server. The server executes the ServeHTTP method that is defined preceding on the mux object.

  1. Run the following command from your shell/Terminal:
go run $GOPATH/src/github.com/git-user/chapter2/uuidGenerator/main.go
  1. We can make a curl request like this to make a request to the web server that is listening on port 8000:
curl -X GET http://localhost:8000/

The response that is returned will be a random string:

544f5519592ac25bb2c0
Use Ctrl + C or Cmd + C to stop your Go server. If you are running it as a background process, use sudo kill `sudo lsof -t -i:8000` to kill a process running on port 8000.

Until now, we have worked with a single handler. Let us see how we can add multiple handlers to route to different function handlers using ServeMux.

Adding multiple handlers using ServeMux

Let us say we have an API requirement that generates random numbers of different types such as int, float, and so on. The custom multiplexer (mux) we developed can be cumbersome when there are multiple endpoints with different functionalities. To add that logic, we need to add multiple if/else conditions to manually check the URL route. To overcome that complex code structure, we can instantiate a new in-built ServeMux object and define many handlers. Let's look at the code with ServeMux:

newMux := http.NewServeMux()

newMux.HandleFunc("/randomFloat", func(w http.ResponseWriter,
r *http.Request) {
fmt.Fprintln(w, rand.Float64())
})

newMux.HandleFunc("/randomInt", func(w http.ResponseWriter,
r *http.Request) {
fmt.Fprintln(w, rand.Int(100))
})

This code snippet shows how to create a ServeMux and attach multiple handlers to it.

randomFloat and randomInt are the two routes we create for returning a random float and random int, respectively. Now, we pass that to the ListenAndServe function. Int(100) returns a random integer number from the range 0-100.

For more details on random functions, visit the Go random package page at: http://golang.org.

Let us see a complete example:

  1. Create a file to hold our program and call it multipleHandlers.go in the following path:
touch -p $GOPATH/src/github.com/git-user/chapter2/multipleHandlers/main.go
  1. Now create a main function and add the code for creating the ServeMux object and function handlers.
  2. Finally, run the server with the http.ListenAndServe method:
package main

import (
"fmt"
"math/rand"
"net/http"
)

func main() {
newMux := http.NewServeMux()
newMux.HandleFunc("/randomFloat", func(w http.ResponseWriter,
r *http.Request) {
fmt.Fprintln(w, rand.Float64())
})
newMux.HandleFunc("/randomInt", func(w http.ResponseWriter,
r *http.Request) {
fmt.Fprintln(w, rand.Intn(100))
})
http.ListenAndServe(":8000", newMux)
}
  1. We can run the program directly using the run command:
go run $GOPATH/src/github.com/git-user/chapter2/multipleHandlers/main.go
  1. Now, let us fire two curl commands and see the output:
curl -X GET http://localhost:8000/randomFloat
curl -X GET http://localhost:8000/randomInt

The responses will be:

0.6046602879796196
87

We saw how we can create a URL router with basic Go constructs. Let us have a look at a few popular URL routing frameworks that are widely used by the Go community for their API servers.