Skip to content

3. Using Mongo DB

Mahir edited this page Jul 23, 2022 · 3 revisions

Final result:

Endpoints:

Add a new address: [HTTP POST] http://localhost:1200/api/v1/new-address

Body:

{
    "person":"",
    "street_name":"",
    "house_number":"",
    "postal_code":"",
    "county":"",
    "city":"",
    "state":"",
    "country":""
}

Find the address of a person: [HTTP GET] http://localhost:1200/api/v1/search-address/:name

Get a list of all records: [HTTP GET] http://localhost:1200/api/v1/all-addresses

Create a project, build, run

This is a continuation of 1. RESTful API: Hello world project.

  • Create a file addresses.go inside controller folder
  • Write Address struct
  • Write a function MongoCreateAddress to addresses.go file
  • Write a function MongoGetAll to addresses.go file
  • Write a function MongoGetAddress to addresses.go file
package controller

import (
	"context"
	"net/http"
	"reflect"
	"strings"
	"time"

	"go.mongodb.org/mongo-driver/bson"
	"go.mongodb.org/mongo-driver/bson/primitive"

	"github.com/gin-gonic/gin"

	"github.com/pilinux/gorest/database"
	"github.com/pilinux/gorest/lib/renderer"
)

// Address - struct for saving addresses
type Address struct {
	ID          primitive.ObjectID `json:"id" bson:"_id"`
	Person      string             `json:"person,omitempty" bson:"person,omitempty"`
	StreetName  string             `json:"street_name,omitempty" bson:"street_name,omitempty"`
	HouseNumber string             `json:"house_number,omitempty" bson:"house_number,omitempty"`
	PostalCode  string             `json:"postal_code,omitempty" bson:"postal_code,omitempty"`
	County      string             `json:"county,omitempty" bson:"county,omitempty"`
	City        string             `json:"city,omitempty" bson:"city,omitempty"`
	State       string             `json:"state,omitempty" bson:"state,omitempty"`
	Country     string             `json:"country,omitempty" bson:"country,omitempty"`
}

// MongoCreateAddress - create one document
func MongoCreateAddress(c *gin.Context) {
	data := Address{}
	if err := c.ShouldBindJSON(&data); err != nil {
		renderer.Render(c, gin.H{"msg": "bad request"}, http.StatusBadRequest)
		return
	}

	// remove all leading and trailing white spaces
	data = trimSpace(data)
	if data.isEmpty() {
		renderer.Render(c, gin.H{"msg": "empty body"}, http.StatusBadRequest)
		return
	}
	if len(data.Person) == 0 {
		renderer.Render(c, gin.H{"msg": "person name is required"}, http.StatusBadRequest)
		return
	}

	// generate a new ObjectID
	data.ID = primitive.NewObjectID()

	client := database.GetMongo()
	db := client.Database("book")            // set database name
	collection := db.Collection("addresses") // set collection name

	// set max TTL
	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
	defer cancel()

	// insert one document
	_, err := collection.InsertOne(ctx, data)
	if err != nil {
		renderer.Render(c, gin.H{"msg": "internal server error"}, http.StatusInternalServerError)
		return
	}

	renderer.Render(c, data, http.StatusCreated)
}

// MongoGetAll - get all documents
func MongoGetAll(c *gin.Context) {
	client := database.GetMongo()
	db := client.Database("book")            // set database name
	collection := db.Collection("addresses") // set collection name

	// set max TTL
	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
	defer cancel()

	data := []Address{}
	err := collection.Find(ctx, bson.M{}).All(&data)
	if err != nil {
		renderer.Render(c, gin.H{"msg": "internal server error"}, http.StatusInternalServerError)
		return
	}
	if len(data) == 0 {
		renderer.Render(c, gin.H{"msg": "no record found"}, http.StatusNotFound)
		return
	}

	renderer.Render(c, data, http.StatusOK)
}

// MongoGetAddress - find the address of a person
func MongoGetAddress(c *gin.Context) {
	name := strings.TrimSpace(c.Params.ByName("name"))
	if len(name) == 0 {
		renderer.Render(c, gin.H{"msg": "person name cannot be empty"}, http.StatusBadRequest)
		return
	}

	client := database.GetMongo()
	db := client.Database("book")            // set database name
	collection := db.Collection("addresses") // set collection name

	// set max TTL
	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
	defer cancel()

	data := []Address{}
	err := collection.Find(ctx, bson.M{"person": name}).All(&data)
	if err != nil {
		renderer.Render(c, gin.H{"msg": "internal server error"}, http.StatusInternalServerError)
		return
	}
	if len(data) == 0 {
		renderer.Render(c, gin.H{"msg": "no record found"}, http.StatusNotFound)
		return
	}

	renderer.Render(c, data, http.StatusOK)
}

// trimSpace - remove all leading and trailing white spaces
func trimSpace(address Address) Address {
	address.Person = strings.TrimSpace(address.Person)
	address.StreetName = strings.TrimSpace(address.StreetName)
	address.HouseNumber = strings.TrimSpace(address.HouseNumber)
	address.PostalCode = strings.TrimSpace(address.PostalCode)
	address.County = strings.TrimSpace(address.County)
	address.City = strings.TrimSpace(address.City)
	address.State = strings.TrimSpace(address.State)
	address.Country = strings.TrimSpace(address.Country)

	return address
}

// isEmpty - check empty struct
func (s Address) isEmpty() bool {
	return reflect.DeepEqual(s, Address{})
}

  • Update the main function in main.go file
package main

import (
	"fmt"

	"myapi/controller"

	"github.com/gin-gonic/gin"
	"github.com/pilinux/gorest/config"
	"github.com/pilinux/gorest/database"
	"github.com/pilinux/gorest/lib/middleware"
)

var configure = config.Config()

func main() {
	if configure.Database.MongoDB.Activate == "yes" {
		// Initialize MONGO client
		if _, err := database.InitMongo(); err != nil {
			fmt.Println(err)
			return
		}
	}

	router, err := SetupRouter()
	if err != nil {
		fmt.Println(err)
		return
	}
	err = router.Run(":" + configure.Server.ServerPort)
	if err != nil {
		fmt.Println(err)
		return
	}
}

// SetupRouter ...
func SetupRouter() (*gin.Engine, error) {
	router := gin.Default()

	router.Use(middleware.CORS(
		configure.Security.CORS.Origin,
		configure.Security.CORS.Credentials,
		configure.Security.CORS.Headers,
		configure.Security.CORS.Methods,
		configure.Security.CORS.MaxAge,
	))
	// For gorest <= v1.4.5
	// router.Use(middleware.CORS())

	// API:v1
	v1 := router.Group("/api/v1/")
	{
		v1.GET("greetings", controller.Greetings)

		if configure.Database.MongoDB.Activate == "yes" {
			v1.POST("new-address", controller.MongoCreateAddress)
			v1.GET("search-address/:name", controller.MongoGetAddress)
			v1.GET("all-addresses", controller.MongoGetAll)
		}
	}

	return router, nil
}

  • Rename the file from .env.sample to .env and modify the following lines:
APP_PORT=1200
ACTIVATE_MONGO=yes
# For MongoDB Atlas
# MONGO_URI=mongodb+srv://<username>:<password>@<cluster>.<subdomain>.mongodb.net/<cluster>?retryWrites=true&w=majority
# For standard connection on the local machine with auth
# MONGO_URI=mongodb://<username>:<password>@<IP>:<PORT>/?retryWrites=true&w=majority

  • Build and start the app

go mod tidy -compat=1.17

go build

chmod +x myapi

./myapi

Project file structure

myapi
│--- main.go
│--- go.mod
│--- go.sum
│--- .env
│
└─── controller
│    └--- greetings.go
│    └--- addresses.go

More examples:

controller/playgroundMongo.go