Build RESTful API service in golang using gin-gonic framework
Today I’m going to build a simple API for todo application with the golang programming language. I’m going to use golang simplest/fastest framework gin-gonic and a beautiful ORM gorm for our database work. To install these packages go to your workspace $GOPATH/src and run these command below:
$ go get gopkg.in/gin-gonic/gin.v1 $go get -u github.com/jinzhu/gorm
$ go get
github.com/go-sql-driver/mysql
In generic crud application we need the API’s as follows:
- POST todos/
- GET todos/
- GET todos/{id}
- PUT todos/{id}
- DELETE todos/{id}
Let’s start coding, go to your $GOPATH/src and make a directory todo. Inside the todo directory create a file main.go. Import the “gin framework” to our project and create the routes like below inside
main
function. I like to add a prefix of the apis like “api/v1/”, that’s why we’ll use the router Group methodpackage main import ( "github.com/gin-gonic/gin" )
func main() {
router := gin.Default()
v1 := router.Group("/api/v1/todos") { v1.POST("/", CreateTodo) v1.GET("/", FetchAllTodo) v1.GET("/:id", FetchSingleTodo) v1.PUT("/:id", UpdateTodo) v1.DELETE("/:id", DeleteTodo) } router.Run()
}
We have created five routes and they handle some functions like
CreateTodo, FetchAllTodo
etc. We’ll discuss about them soon.
Now we need to setup a database connection. To use database pull the gorm package and mysql dialects in our code. Follow the code below:
package main import ( "github.com/gin-gonic/gin" "github.com/jinzhu/gorm" _ "github.com/jinzhu/gorm/dialects/mysql" ) func Database() *gorm.DB { //open a db connection db, err := gorm.Open("mysql", "root:12345@/demo?charset=utf8&parseTime=True&loc=Local") if err != nil { panic("failed to connect database") } return db }
In the above code “mysql” is our database driver, “root” is database username, “12345” password and “demo” is database name. Please change these information as your needs.
We’ll use the Database function to get the database connection. Lets make a Todo and TransformedTodo struct. The first struct will represent the original Todo and the second one will hold the transformed todo for response to the api. Here we transformed the todo response because we don’t expose some database fields (updated_at, created_at) to the consumer.
type Todo struct { gorm.Model Title string `json:"title"` Completed int `json:"completed"` } type TransformedTodo struct { ID uint `json:"id"` Title string `json:"title"` Completed bool `json:"completed"` }
Todo struct has one field extra
gorm.Model
what does it mean? well, this field will embed a Model struct for us which contains four fields “ID, CreatedAt, UpdatedAt, DeletedAt”
Gorm has migration facilities, we’ll use it now. Let add four more lines inside
main
function at very begining.//Migrate the schemadb := Database() db.AutoMigrate(&Todo{})
Can you remember the five routes we wrote a minute earlier? Lets implement the five methods one by one.
When a user send a POST request to the path ‘api/v1/todos/’ with ‘title and completed’ field it’ll be handled by this route
v1.POST(“/”, CreateTodo)
Lets Implement the CreateTodo function
func CreateTodo(c *gin.Context) { completed, _ := strconv.Atoi(c.PostForm("completed")) todo := Todo{Title: c.PostForm("title"), Completed: completed}; db := Database() db.Save(&todo) db.Close() c.JSON(http.StatusCreated, gin.H{"status" : http.StatusCreated, "message" : "Todo item created successfully!", "resourceId": todo.ID}) }
In the above code we use gin Context to receive the posted data and gorm database connection to save the todo. After saving the resource we send the
resource id
with a good & meaningful response to the user.
Lets implement the rest of the functions
func FetchAllTodo(c *gin.Context) { var todos []Todo var _todos []TransformedTodo db := Database() db.Find(&todos) if (len(todos) <= 0) { c.JSON(http.StatusNotFound, gin.H{"status" : http.StatusNotFound, "message" : "No todo found!"}) return } //transforms the todos for building a good response for _, item := range todos { completed := false if (item.Completed == 1) { completed = true } else { completed = false } _todos = append(_todos, TransformedTodo{ID: item.ID, Title:item.Title, Completed: completed}) } c.JSON(http.StatusOK, gin.H{"status" : http.StatusOK, "data" : _todos}) } func FetchSingleTodo(c *gin.Context) { var todo Todo todoId := c.Param("id") db := Database() db.First(&todo, todoId) if (todo.ID == 0) { c.JSON(http.StatusNotFound, gin.H{"status" : http.StatusNotFound, "message" : "No todo found!"}) return } completed := false if (todo.Completed == 1) { completed = true } else { completed = false } _todo := TransformedTodo{ID: todo.ID, Title:todo.Title, Completed: completed} c.JSON(http.StatusOK, gin.H{"status" : http.StatusOK, "data" : _todo}) } func UpdateTodo(c *gin.Context) { var todo Todo todoId := c.Param("id") db := Database() db.First(&todo, todoId) if (todo.ID == 0) { c.JSON(http.StatusNotFound, gin.H{"status" : http.StatusNotFound, "message" : "No todo found!"}) return } db.Model(&todo).Update("title", c.PostForm("title")) db.Model(&todo).Update("completed", c.PostForm("completed")) db.Close() c.JSON(http.StatusOK, gin.H{"status" : http.StatusOK, "message" : "Todo updated successfully!"}) } func DeleteTodo(c *gin.Context) { var todo Todo todoId := c.Param("id") db := Database() db.First(&todo, todoId) if (todo.ID == 0) { c.JSON(http.StatusNotFound, gin.H{"status" : http.StatusNotFound, "message" : "No todo found!"}) return } db.Delete(&todo) c.JSON(http.StatusOK, gin.H{"status" : http.StatusOK, "message" : "Todo deleted successfully!"}) }
In the FetchAllTodo function we fetched all the todos and and build a transformed response with
id, title, completed
. We removed the CreatedAt, UpdatedAt, DeletedAt fields and cast the integer value to bool.
Well, we write enough code, let try to build the app and test it, I’m going test it using chrome extension Postman (you can use any REST client like curl to test).
To build the app open your terminal and go the the project directory
$ go build main.go
The command will build a binary file
main
and to run the file us this command $ ./main
. It’ll display the debug log, because by default gin
run’s in debug mode.
To test the api run postman and test the api sequentially
reference : https://medium.com/@thedevsaddam/build-restful-api-service-in-golang-using-gin-gonic-framework-85b1a6e176f3
留言
張貼留言