At Agira, Technology Simplified, Innovation Delivered, and Empowering Business is what we are passionate about. We always strive to build solutions that boost your productivity.

MongoDB In Golang With Examples – A Beginner's Guide

  • By Reddy Sai
  • March 4, 2020
  • 17780 Views

In this tutorial, we will learn how to connect MongoDB With Golang. We will also look into how to save, update and delete records in MongoDB using Go. Finally to cover CRUD operations with MongoDB and Go, we will be creating a simple Web application.
MongoDB is used to save records in a JSON document format. The fields may vary from one record to another.
Let’s take a brief look at what you will be learning from this tutorial.

  • Starting and Setup
  • Connect to the MongoDB
  • Save Documents
  • Read Documents
  • Update Documents
  • Remove Documents

Prerequisites

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

To cover all the mentioned topics, we are going to create a sample web application for demo.

Getting Started and Setup

Create a folder go-mongo or any other prefer as the project name and add a main.go file with the below sample code to the project.
main.go

package main
import (
           “fmt”
)
func main() {
            fmt.Println(“Setup Go-Mongo project”)
}

For this tutorial, all the source code will be added to the same main.go file to avoid confusion later on code pattern and folder structure.
With the help of the below command, you can create a module file.

$ go mod init go-mongo

This command will create a go.mod file in the root directory of the project.
To interact with MongoDB, we need a package that will provide MongoDB driver for Go. Here, we go with the mgo package. The package mgo offers a rich MongoDB driver for Go.
To get the mgo package, execute the below command from your terminal.

$ go get gopkg.in/mgo.v2

After completion of the above command execution, the go.mod file will be updated with the package name and version number automatically. Now the file is created with the go.sum which is containing the expected cryptographic checksums for each package.

Related to MongoDB

10 Tips To Improve Your MongoDB Security

Connect to the MongoDB

To interact with the database, we have to create the connection session to the database. With the help of the mgo package, we have to create a session for MongoDB using Go. In the below snippet, I have created a function called connect() which is actually creating a session with the local MongoDB and returning it.

// DB connection
func connect() *mgo.Session {
session, err := mgo.Dial("localhost")
if err != nil {
fmt.Println("session err:", err)
os.Exit(1)
}
return session
}

I have given the hostname as localhost and passing it to the Dial function. The Dial function will establish the MongoDB connection and return the session information. In case, if there is anything wrong with the host or database, it will throw the error.
In this practice, the database connection is must so, if there is an error it should be removed from the service. To avoid database error, make sure that the MongoDB is installed in the system.
Instead of local MongoDB, you can also make a connection with the clustered database.

Save Documents

Now, the database connection has occurred successfully. We have to do the database operations like Insert, update and delete. To do these operations, firstly we will set up a routes service.

// StartService function
func StartService() {
    router := gin.Default()
    api := router.Group("/api")
    {
        // Create user record
        api.POST("/users", func(c *gin.Context) {
            c.JSON(http.StatusOK,
            gin.H{
                "status": "success",
                "user": {},
            })
        })
    }
    router.NoRoute(func(c *gin.Context) {
        c.AbortWithStatus(http.StatusNotFound)
    })
    router.Run(":8000")
}

I have created a function called StartService with the sample route using the Gin package. To get the gin package, use the below command.

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

If you notice the service function, I have grouped the /users route with the /api as a base path of the route. And the route is responding with success status with the empty user object. Here, instead of an empty object, we have to respond to the saved user object.
For the user object, we have to create the user struct with the matched/expected collection keys.

// 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"`
}

Here, I have mentioned a few of the needed record keys using relevant data types in the User struct.
Inside the POST:  /users route declare the variable using the User custom data type and bind the request body to the same. And, create a unique Mongo Object ID to the user variable. Assign the created and updated date & time to user object variable like below,

user := User{}
err := c.Bind(&user)
user.ID = bson.NewObjectId()
user.CreatedAt, user.UpdatedAt = time.Now(), time.Now()
if err != nil {
            c.JSON(http.StatusBadRequest,
  gin.H{
"status": "failed",
"message": "Invalid request body",
  })
return
}

The NewObjectId will be useful to get the Mongo unique Object ID. Import the mgo bson package gopkg.in/mgo.v2/bson to use NewObjectId function.
We have assigned the request body parameters to the user object. Now, we have to insert the record into the database.
Before we make the connection with the database we have inserted the record into it, declare required information about database and collection names globally.

// database and collection names are statically declared
const database, collection = "go-mongo-practice", "user"

Now, create the MongoDB session and insert the record into the database with the help of mgo Insert function.

session := connect()
defer session.Close()
err = session.DB(database).C(collection).Insert(user)

For the complete code of the document, insertion looks like below.

// Create user record
api.POST("/users", func(c *gin.Context) {
user := User{}
err := c.Bind(&user)
if err != nil {
          c.JSON(http.StatusBadRequest,
gin.H{
"status": "failed",
"message": "Invalid request body",
})
return
}
user.ID = bson.NewObjectId()
user.CreatedAt, user.UpdatedAt = time.Now(), time.Now()
session := connect()
defer session.Close()
err = session.DB(database).C(collection).Insert(user)
if err != nil {
c.JSON(http.StatusBadRequest,
gin.H{
"status": "failed",
"message": "Error in the user insertion",
})
return
}
c.JSON(http.StatusOK,
gin.H{
"status": "success",
"user": &user,
})
})

The route will respond with the saved user object. If there is an error with insertion or invalid body, it will respond with the failed status along with the message.
Declare the main function to start our service and test the user route to save the document.

func main() {
StartService()
}

To start the service, run the main.go file like below,

$ go run main.go

Here, is the full source of main.go file.

package main
import (
"fmt"
"net/http"
"os"
"time"
"github.com/gin-gonic/gin"
mgo "gopkg.in/mgo.v2"
"gopkg.in/mgo.v2/bson"
)
// database and collection names are statically declared
const database, collection = "go-mongo-practice", "user"
// 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"`
}
// DB connection
func connect() *mgo.Session {
session, err := mgo.Dial("localhost")
if err != nil {
fmt.Println("session err:", err)
os.Exit(1)
}
return session
}
// StartService function
func StartService() {
router := gin.Default()
api := router.Group("/api")
{
// Create user record
api.POST("/users", func(c *gin.Context) {
user := User{}
err := c.Bind(&user)
if err != nil {
c.JSON(http.StatusBadRequest,
gin.H{
"status": "failed",
"message": "Invalid request body",
})
return
}
user.ID = bson.NewObjectId()
user.CreatedAt, user.UpdatedAt = time.Now(), time.Now()
session := connect()
defer session.Close()
err = session.DB(database).C(collection).Insert(user)
if err != nil {
c.JSON(http.StatusBadRequest,
gin.H{
"status": "failed",
"message": "Error in the user insertion",
})
return
}
c.JSON(http.StatusOK,
gin.H{
"status": "success",
"user": &user,
})
})
}
router.NoRoute(func(c *gin.Context) {
c.AbortWithStatus(http.StatusNotFound)
})
router.Run(":8000")
}
func main() {
StartService()
}

Use the request body as like below to save the document in the MongoDB through the users API route POST: http://localhost:8000/users 

{
"name": "John",
"address": "387 Main Street, Bolivar, NY",
"age": 26
}

Read Documents

To retrieve the save documents from the Mongo database, we have to build another route to read saved documents from the database.
With the help of mgo Find method, we can retrieve all saved documents from the database. Follow the below code snippet to do it.

// List users
api.GET("/users", func(c *gin.Context) {
users := Users{}
session := connect()
defer session.Close()
err := session.DB(database).C(collection).Find(bson.M{}).All(&users)
if err != nil {
c.JSON(http.StatusNotFound,
                                    gin.H{
                                               "status": "failed",
                                               "message": "Users are not exist",
                                    })
            return
}
c.JSON(http.StatusOK, gin.H{"status": "success", "users": &users})
})

In that above GET: /users route, I have declared the users variable with Users custom data type. Declare a Users struct globally like below,

// Users list
type Users []User

This Users struct variable will help us to hold the array of saved user documents. After session establishment, find all user documents and assign them to the users variable with the help of mgo Find and All function as mentioned in the route function.
To test this new route to list saved documents, restart the service and call the API route GET: http://localhosts:8000/users

Update Documents

To modify the existing document in the DB, we have to build another route to interact with the database and find the corresponding document with the help of a unique ID and to modify the existing one with the new content.
Modify the existing StartService function with the below code snippet,

// Update user record
api.PUT("/users/:id", func(c *gin.Context) {
var id bson.ObjectId = bson.ObjectIdHex(c.Param("id")) // Get Param
existingUser, err := getUser(id)
if err != nil {
c.JSON(http.StatusBadRequest,
                                    gin.H{
                                               "status": "failed",
                                               "message": "Invalid ID",
                                     })
return 
}
err = c.Bind(&existingUser)
if err != nil {
c.JSON(http.StatusBadRequest,
gin.H{
"status": "failed",
"message": "Invalid request body",
})
return
}
existingUser.UpdatedAt = time.Now()
session := connect()
defer session.Close()
err = session.DB(database).C(collection).Update(bson.M{"_id": &id}, existingUser)
if err != nil {
c.JSON(http.StatusBadRequest,
                                    gin.H{
                                               "status": "failed",
                                              "message": "Error in the user updation",
                                    })
return
}
c.JSON(http.StatusOK, gin.H{"status": "success", "user": &existingUser})
})

To modify the existing document, we have to expect something the unique value of the corresponding record. Here, I have mentioned id as an expected URI parameter. With the help of this ID, we can check whether the record exists or not. If it exists, we can easily update the document based on the request body.
Whatever exists in the URL will be string data type by default. But, in our database, the id type is a Mongo ObjectID. So, we have to convert it into the actual format. The mgo bson package provides the function ObjectIdHex to an ObjectId from the provided hex string.
With the help of converted ID, we have to check the document whether it exists or not. To read the document with the help of ID, we have to create a function getUser.

// getUser function
func getUser(id bson.ObjectId) (User, error) {
user := User{}
session := connect()
defer session.Close()
err := session.DB(database).C(collection).Find(bson.M{"_id": &id}).One(&user)
return user, err
}

After calling this function, we are assigning the existing user record to the existing user variable. And, binding the request body to the same variable to replace the existing user key values which needed to update in the database.
With the help of mgo Update function, we are updating the corresponding document with new changes.
To test this new route to update the existing document, restart the service and call the API route PUT: http://localhost:8000/users/:id with the modified request body.

Related to MongoDB

How To Find Unused Indexes In MongoDB

Remove Documents

To remove the existing document from the database, we have to create a new route to find the corresponding document with the help of ID and delete it from our database. Include the below route source inside the StartService function.

// Remove user record
api.DELETE("/users/:id", func(c *gin.Context) {
var id bson.ObjectId = bson.ObjectIdHex(c.Param("id")) // Get Param
session := connect()
defer session.Close()
err := session.DB(database).C(collection).Remove(bson.M{"_id": &id})
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"status": "failed", "message": "Error in the user deletion"})
return
}
c.JSON(http.StatusOK, gin.H{"status": "success", "message": "User deleted successfully"})
})

The DELETE route, we are converting the provided hex ID into the Mongo ObjectId with the help of ObjectIdHex function.
By using the Remove function, we are removing the corresponding document from our database.
To test this new route to delete the existing document, restart the service and call the API route DELETE: http://localhost:8000/users/:id

Conclusion

I hope this tutorial helps to set up the sample Golang application with MongoDB operations. If you are new to this concept this also helps you understand MongoDB CRUD operations. You can find the complete source from golang-mongo.
Like to have some more Tutorials and updates? Subscribe to our weekly newsletter to stay updated and learn techniques for various development technologies.

Turn your vision to magnificent reality With
Our Web and Mobile Solutions