Building RESTful APIs with the Gin framework

Gin-gonic is a framework based on the httprouter. We learned about the httprouter in Chapter 2, Handling Routing for Our REST Services. It is an HTTP multiplexer like Gorilla Mux, but it is faster. Gin allows a high-level API to create REST services in a clean way. Gin compares itself with another web framework called martini. All web frameworks allow us to do a lot more things such as templating and web server design, additional to service creation. Install the Gin package using the following command:

go get gopkg.in/gin-gonic/gin.v1

Let us write a simple hello world program in Gin to get familiarized with the Gin constructs. The file is ginBasic.go:

package main
import (
"time"
"github.com/gin-gonic/gin"
)
func main() {
r := gin.Default()
/* GET takes a route and a handler function
Handler takes the gin context object
*/
r.GET("/pingTime", func(c *gin.Context) {
// JSON serializer is available on gin context
c.JSON(200, gin.H{
"serverTime": time.Now().UTC(),
})
})
r.Run(":8000") // Listen and serve on 0.0.0.0:8080
}

This simple server tries to implement a service that provides the UTC server time to the clients. We implemented one such service in Chapter 3Working with Middleware and RPC. But here, if you look, Gin allows you to do a lot of stuff with just a few lines of code; all the boilerplate details are taken away. Coming to the preceding program, we are creating a router with the gin.Default function. Then, we are attaching routes with REST verbs as we did in go-restful; a route to the function handler. Then, we are calling the Run function by passing the port to run. The default port will be 8080.

c is the gin.Context that holds the information of the individual request. We can serialize data into JSON before sending it back to the client using the context.JSON function. Now, if we run and see the preceding program:

go run ginExamples/ginBasic.go

Make a curl request:

CURL -X GET "http://localhost:8000/pingTime"

Output
=======
{"serverTime":"2017-06-11T03:59:44.135062688Z"}

At the same time, the console where we are running the Gin server is beautifully presented with debug messages:

It is Apache-style debug logging showing the endpoint, the latency of the request, and the REST method.

In order to run Gin in production mode, set the GIN_MODE = release environment variable. Then the console output will be muted and log files can be used for monitoring the logs.

Now, let us write our Rail API in Gin to show how to implement exactly the same thing using the Gin framework. I will use the same project layout, name my new project railAPIGin, and use the dbutils as it is. First, let us prepare the imports for our program:

package main
import (
"database/sql"
"log"
"net/http"
"github.com/gin-gonic/gin"
_ "github.com/mattn/go-sqlite3"
"github.com/narenaryan/dbutils"
)

We imported sqlite3 and dbutils for database-related actions. We imported gin for creating our API server. net/http is useful in providing the intuitive status codes to be sent along with the response. Take a look at the following code snippet:

// DB Driver visible to whole program
var DB *sql.DB
// StationResource holds information about locations
type StationResource struct {
ID int `json:"id"`
Name string `json:"name"`
OpeningTime string `json:"opening_time"`
ClosingTime string `json:"closing_time"`
}

We created a database driver that is available to all handler functions. StationResource is the placeholder for our JSON that decoded from both request body and data coming from the database. In case you noticed, it is slightly modified from the example of go-restful. Now, let us write the handlers implementing GET, POST, and DELETE  methods for the station resource:

// GetStation returns the station detail
func GetStation(c *gin.Context) {
var station StationResource
id := c.Param("station_id")
err := DB.QueryRow("select ID, NAME, CAST(OPENING_TIME as CHAR), CAST(CLOSING_TIME as CHAR) from station where id=?", id).Scan(&station.ID, &station.Name, &station.OpeningTime, &station.ClosingTime)
if err != nil {
log.Println(err)
c.JSON(500, gin.H{
"error": err.Error(),
})
} else {
c.JSON(200, gin.H{
"result": station,
})
}
}
// CreateStation handles the POST
func CreateStation(c *gin.Context) {
var station StationResource
// Parse the body into our resrource
if err := c.BindJSON(&station); err == nil {
// Format Time to Go time format
statement, _ := DB.Prepare("insert into station (NAME, OPENING_TIME, CLOSING_TIME) values (?, ?, ?)")
result, _ := statement.</span>Exec(station.Name, station.OpeningTime, station.ClosingTime)
if err == nil {
newID, _ := result.LastInsertId()
station.ID = int(newID)
c.JSON(http.StatusOK, gin.H{
"result": station,
})
} else {
c.String(http.StatusInternalServerError, err.Error())
}
} else {
c.String(http.StatusInternalServerError, err.Error())
}
}
// RemoveStation handles the removing of resource
func RemoveStation(c *gin.Context) {
id := c.Param("station-id")
statement, _ := DB.Prepare("delete from station where id=?")
_, err := statement.Exec(id)
if err != nil {
log.Println(err)
c.JSON(500, gin.H{
"error": err.Error(),
})
} else {
c.String(http.StatusOK, "")
}
}

In GetStation, we are using the c.Param to strip the station_id path parameter. After that, we are using that ID to fetch a database record from the SQLite3 station table. If you observed carefully, the SQL query is bit different. We are using the CAST method to retrieve the SQL TIME field as a string for Go to consume properly. If you remove the casting, a panic error will be raised because we are trying to load a TIME field into the Go string at runtime. To give you an idea, the TIME field looks like 8:00:00, 17:31:12, and so on. Next, we are returning back the result using the gin.H method if there is no error.

In CreateStation, we are trying to perform an insert query. But before that, in order to get data from the body of the POST request, we are using a function called c.BindJSON. This function loads the data into the struct that is passed as the argument. It means the station struct will be loaded with the data supplied from the body. That is why StationResource has the JSON inference strings to tell what key values are expected. For example, this is such field of StationResource struct with inference string.

ID int `json:"id"`

After collecting the data, we are preparing a database insert statement and executing it. The result is the ID of the inserted record. We are using that ID to send station details back to the client. In RemoveStation, we are performing a DELETE SQL query. If the operation was successful, we return a 200 OK status back. Otherwise, we are sending the appropriate reason for a 500 Internal Server Error.

Now comes the main program, which runs the database logic first to make sure tables are created. Then, it tries to create a Gin router and adds routes to it:

func main() {
var err error
DB, err = sql.Open("sqlite3", "./railapi.db")
if err != nil {
log.Println("Driver creation failed!")
}
dbutils.Initialize(DB)
r := gin.Default()
// Add routes to REST verbs
r.GET("/v1/stations/:station_id", GetStation)
r.POST("/v1/stations", CreateStation)
r.DELETE("/v1/stations/:station_id", RemoveStation)
r.Run(":8000") // Default listen and serve on 0.0.0.0:8080
}

We are registering the GET, POST, and DELETE routes with the Gin router. Then, we are passing routes and handlers to them. Finally, we are starting the server using the Run function of Gin with 8000 as the port. Run the preceding program, as follows:

go run railAPIGin/main.go

Now, we can insert a new record by performing a POST request:

curl -X POST 
http://localhost:8000/v1/stations
-H 'cache-control: no-cache'
-H 'content-type: application/json'
-d '{"name":"Brooklyn", "opening_time":"8:12:00", "closing_time":"18:23:00"}'

It returns:

{"result":{"id":1,"name":"Brooklyn","opening_time":"8:12:00","closing_time":"18:23:00"}}

And now try to fetch the details using GET:

CURL -X GET "http://10.102.78.140:8000/v1/stations/1"

Output
======
{"result":{"id":1,"name":"Brooklyn","opening_time":"8:12:00","closing_time":"18:23:00"}}

We can also delete the station record using the following command:

CURL -X DELETE "http://10.102.78.140:8000/v1/stations/1"

It returns a 200 OK status, confirming the resource was successfully deleted. As we already discussed, Gin provides intuitive debugging on the console, showing the attached handler and highlighting the latency and REST verbs with colors:

For example, a 200 is green, a 404 is yellow, DELETE is red, and so on. Gin provides many other features such as the categorization of routes, redirects, and middleware functions. 

Use the Gin framework if you are quickly prototyping a REST web service. You can also use it for many other things such as static file serving and so on. Remember that it is a fully-fledged web framework. For fetching the query parameters in Gin, use the following method on the Gin context object: c.Query("param").
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.141.37.10