Build RESTful API service in golang using gin-gonic framework

Today I’m going to build a simple API fortodoapplication with the golang programming language. I’m going to use golang simplest/fastest frameworkgin-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:

  1. POST todos/
  2. GET todos/
  3. GET todos/{id}
  4. PUT todos/{id}
  5. DELETE todos/{id}

Let’s start coding, go to your $GOPATH/src and make a directorytodo.Inside the todo directory create a filemain.go.Import the “gin framework” to our project and create the routes like below insidemainfunction. I like to add a prefix of the apis like “api/v1/”, that’s why we’ll use the router Group method

package 
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 likeCreateTodo, FetchAllTodoetc. 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"


)




type DB struct 
{


*
gorm.DB


}




func Database
() (
*DB
, error) {


//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
{db}, nil


}

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 aTodoandTransformedTodostruct. 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 extragorm.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 insidemainfunction at very begining.

//Migrate the schema


db, _ 
:= Database
()


defer 
db.Close()


db.AutoMigrate(
&
Todo
{})

Using phpmyadmin

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 routev1.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
()


defer 
db.Close()


       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 theresource 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)


       db.Close()




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)


       db.Close()




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)


       db.Close()


       c.JSON(http.
StatusOK
, gin.H{"status" : http.
StatusOK
, "message" : "Todo deleted successfully!"})


}

In theFetchAllTodofunction we fetched all the todos and and build a transformed response withid, 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 filemainand to run the file us this command$ ./main . It’ll display the debug log, because by defaultginrun’s in debug mode.

To test the api run postman and test the api sequentially

Create a todo

Fetch all todos

Fetch a single todo

Update a todo

Delete a todo

Note: When you are using code production you must take care of the steps below:

  1. Do not fetch all the data select * from todos
  2. Do not trust user input. You must validate the inputs
  3. Check every possible error

I am really sorry for my_BAD_English and Writing flow. If you notice any mistake in the article please feel free to write a comment.

If you find the article helpful please follow me in
github/thedevsaddam

You may read from here or bookmark: https://medium.com/@thedevsaddam/build-restful-api-service-in-golang-using-gin-gonic-framework-85b1a6e176f3\#.cfo7b0yzj

results matching ""

    No results matching ""