let’s make something together

Give us a call or drop by anytime, we endeavour to answer all enquiries within 24 hours on business days.

Find us

PO Box 16122 Collins Street West
Victoria 8007 Australia

Email us

info@domain.com
example@domain.com

Phone support

Phone: + (066) 0760 0260
+ (057) 0760 0560

How to Build RESTful API Service In Golang using Gin-Gonic Framework

  • By Reddy Sai
  • February 12, 2020
  • 2997 Views

Today, we will walk through a tutorial on how to build a RESTful API service with simple CRUD operations in the Golang using Gin-Gonic framework. Gin allows a high-level API to create RESTful services in a clean way.

What is the RESTful API service?

Representations State Transfer (REST) is an architectural style that enables the communication between systems. The concept is defined by certain principles and rules. Certain things that satisfy those REST principles are called RESTful. Web services that follow the RESTful principles are RESTful services. It is used to build lightweight, maintainable, scalable WEB services.

restful-web-services-api-architecture
Source: Phppot

There are so many popular web frameworks for Golang. Here some of the most popular frameworks.

  1. Gin
  2. Beego
  3. Gorilla Mux
  4. Net/HTTP
  5. Buffalo

Now, let’s start building a RESTful API service using the GIN framework with Go modules. Here I’m using MangiDB for data storage in the objective format.

Prerequisites

  • Go 1.13( >= 1.11)
  • MongoDB 4.2 (or MongoDB Atlas)

Project Repository Structure

├── conn

│ └── mongo.go

├── controllers

│ └── users

│ └── users.go

├── models

│ └── user

│ └── user.go

├── routes

│ └── routes.go

├── go.mod

├── go.sum

├── main.go

└── .env

Create a folder called CRUD-Operations and check into the folder. Generate a module file using the below command.

Note*: To work with modules, we have to maintain the latest version or version go 1.11.

$ go mod init CRUD-Operation

The above command will create a go.mod file in the working directory with the current version like,

module CRUD-Operation
go 1.13

Create a main.go file inside the root directory with the help of the below source.

package main
import(
     routes "CRUD-Operation/routes"
)
func main() {
     routes.StartService()
}

After, we will be importing routes packages. The routes package contains a StartService function.

Creating Routes

Let’s create a routes file with the below source under the name of routes.go inside the routes directory.

routes/routes.go

package routes
import (
       "net/http"
       user "CRUD-Operation/controllers/user"
       "github.com/gin-gonic/gin"
)
//StartGin function
func StartGin() {
       router := gin.Default()
       api := router.Group("/api")
       {
                api.GET("/users", user.GetAllUser)
                api.POST("/users", user.CreateUser)
                api.GET("/users/:id", user.GetUser)
                api.PUT("/users/:id", user.UpdateUser)
                api.DELETE("/users/:id", user.DeleteUser)
       }
       router.NoRoute(func(c *gin.Context) {
              c.AbortWithStatus(http.StatusNotFound)
       })
       router.Run(":8000")
}

The above package function contains a list of API endpoints. I have grouped all endpoints with the API prefix. You must have noticed that I’ve Gin packages imported in the routes script. You can also do the same.

To get install this package in the system, use the below command.

$ go get github.com/gin-gonic/gin
Or
$ go get

To confirm the installation, check the go.mod and go.sum files.

Once the StartGin function called from the main.go file, Gin service will start with the 8000 port. For now, this service will fail with invalid package error. Since I did not create any user controller package. You can create one while you practice.

Creating User Controller

Make sure to finish the coding part before you start with the service. Create a controllers folder in the root directory to keep all the controller logic. And, also create a user.go file inside the users directory under the controllers directory. It is will easier if you are following the project structure just as we discussed in the beginning.

controllers/users/users.go

package user
import (
        "errors"
        "net/http"
        "time"
        "CRUD-Operation/conn"
        user "CRUD-Operation/models/user"
        "github.com/gin-gonic/gin"
        "gopkg.in/mgo.v2/bson"
)
// UserCollection statically declared
const UserCollection = "user"
var (
       errNotExist        = errors.New("Users are not exist")
       errInvalidID       = errors.New("Invalid ID")
       errInvalidBody     = errors.New("Invalid request body")
       errInsertionFailed = errors.New("Error in the user insertion")
       errUpdationFailed  = errors.New("Error in the user updation")
       errDeletionFailed  = errors.New("Error in the user deletion")
)
// GetAllUser Endpoint
func GetAllUser(c *gin.Context) {
       // Get DB from Mongo Config
       db := conn.GetMongoDB()
       users := user.Users{}
       err := db.C(UserCollection).Find(bson.M{}).All(&users)
       if err != nil {
                 c.JSON(http.StatusBadRequest, gin.H{"status": "failed", "message": errNotExist.Error()})
                 return
       }
       c.JSON(http.StatusOK, gin.H{"status": "success", "users": &users})
}
// GetUser Endpoint
func GetUser(c *gin.Context) {
       var id bson.ObjectId = bson.ObjectIdHex(c.Param("id")) // Get Param
       user, err := user.UserInfo(id, UserCollection)
       if err != nil {
                  c.JSON(http.StatusBadRequest, gin.H{"status": "failed", "message": errInvalidID.Error()})
                  return
       }
       c.JSON(http.StatusOK, gin.H{"status": "success", "user": &user})
}
// CreateUser Endpoint
func CreateUser(c *gin.Context) {
       // Get DB from Mongo Config
       db := conn.GetMongoDB()
       user := user.User{}
       err := c.Bind(&user)
       if err != nil {
                  c.JSON(http.StatusBadRequest, gin.H{"status": "failed", "message": errInvalidBody.Error()})
                  return
       }
       user.ID = bson.NewObjectId()
       user.CreatedAt = time.Now()
       user.UpdatedAt = time.Now()
       err = db.C(UserCollection).Insert(user)
       if err != nil {
                  c.JSON(http.StatusBadRequest, gin.H{"status": "failed", "message": errInsertionFailed.Error()})
                  return
       }
       c.JSON(http.StatusOK, gin.H{"status": "success", "user": &user})
}
// UpdateUser Endpoint
func UpdateUser(c *gin.Context) {
       // Get DB from Mongo Config
       db := conn.GetMongoDB()
       var id bson.ObjectId = bson.ObjectIdHex(c.Param("id")) // Get Param
       existingUser, err := user.UserInfo(id, UserCollection)
       if err != nil {
                  c.JSON(http.StatusBadRequest, gin.H{"status": "failed", "message": errInvalidID.Error()})
                  return
       }
       // user := user.User{}
       err = c.Bind(&existingUser)
       if err != nil {
                 c.JSON(http.StatusBadRequest, gin.H{"status": "failed", "message": errInvalidBody.Error()})
                  return
       }
       existingUser.ID = id
       existingUser.UpdatedAt = time.Now()
       err = db.C(UserCollection).Update(bson.M{"_id": &id}, existingUser)
       if err != nil {
                  c.JSON(http.StatusBadRequest, gin.H{"status": "failed", "message": errUpdationFailed.Error()})
                  return
       }
       c.JSON(http.StatusOK, gin.H{"status": "success", "user": &existingUser})
}
// DeleteUser Endpoint
func DeleteUser(c *gin.Context) {
       // Get DB from Mongo Config
       db := conn.GetMongoDB()
       var id bson.ObjectId = bson.ObjectIdHex(c.Param("id")) // Get Param
       err := db.C(UserCollection).Remove(bson.M{"_id": &id})
       if err != nil {
                  c.JSON(http.StatusBadRequest, gin.H{"status": "failed", "message": errDeletionFailed.Error()})
                  return
       }
       c.JSON(http.StatusOK, gin.H{"status": "success", "message": "User deleted successfully"})
}

In the above controller file, It will be handling requests and responses. For the database to work properly, you have to import a user model package.

Creating a User Model

Create a user.go file inside the user directory and keep the file inside models directory.

package model
import (
	"CRUD-Operation/conn"
	"time"
	"gopkg.in/mgo.v2/bson"
)
// User structure
type User struct {
	ID        bson.ObjectId `bson:"_id"`
	Name      string        `bson:"name"`
	Address   string        `bson:"address"`
	Age       int           `bson:"age"`
	CreatedAt time.Time     `bson:"created_at"`
	UpdatedAt time.Time     `bson:"updated_at"`
}
// Users list
type Users []User
// UserInfo model function
func UserInfo(id bson.ObjectId, userCollection string) (User, error) {
	// Get DB from Mongo Config
	db := conn.GetMongoDB()
	user := User{}
	err := db.C(userCollection).Find(bson.M{"_id": &id}).One(&user)
	return user, err
}

Database connection with MongoDB

To make a connection with the database, create a connection file inside the conn directory.

/conn/mongo.go

package conn
import (
        "fmt"
        "os"
        mgo "gopkg.in/mgo.v2"
)
var db *mgo.Database
func init() {
        host := os.Getenv("MONGO_HOST")
        dbName := os.Getenv("MONGO_DB_NAME")
        session, err := mgo.Dial(host)
        if err != nil {
                  fmt.Println("session err:", err)
                  os.Exit(2)
        }
        db = session.DB(dbName)
}
// GetMongoDB function to return DB connection
func GetMongoDB() *mgo.Database {
       return db
}

In this conn package, I have given the desirable environment variable values for MONGO_HOST and MONGO_DB_NAME. Follow the below steps to setup environment variables.

Setup environment variables

Create a .env file with the below key-pair values.

export MONGO_HOST="localhost"
export MONGO_DB_NAME="go-mongo"

Run the below command to setup environment variables.

$ source .env

As I have completed the coding with the Gin-gonic package, Now, it’s time to test the service routes.

How to build and Run service?

Generate the executable file using the below command at the project root directory.

$ go build

It will generate an executable file with the name of the project root directory name. Run the executable file to start the service.

$ ./CRUD-Operation

Now, you can notice the log with the list of service routes. You can test APIs by using http://localhost:8000 host with the corresponding service route and method.

Conclusion

I hope you find this tutorial useful! In this blog, we have learned how to use the Gin-Gonic package to create RESTful API services in Golang. And also building MongoDB connection and Database works with the help of mgo.v2 package. Find the full source code in this link.

Loved this Tutorial? Read another interesting one! Golang Testing Using Ginkgo.

Need some extra Golang vibes? Take a look at our other popular blogs on Golang development. Don’t forget to subscribe to the newsletter!

Looking to start your next project? Without taking it any longer, get started by hiring Golang developers at Agira. As a top-notch Golang development company, we provide our clients with innovative technology solutions using Golang. Talk to our experts today!

Get to Know how Much it Costs to Develop your Business Application with out Intelligent App Cost Calculator
Check Out