Table of Contents
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.
There are so many popular web frameworks for Golang. Here some of the most popular frameworks.
- Gin
- Beego
- Gorilla Mux
- Net/HTTP
- 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!