Thursday 19 September 2019

CRUD API For MongoDB using GoLang

Introduction

While MongoDB has established itself as a preferred NoSQL database in last few years, Go language or in short Go, developed and backed by Google has gone up the ladder of popularity as a mainstay programming language during the same period almost. Being a beginner to both the worlds, today I will show you how to develop API for MongoDB CRUD operations using Go and official MongoDB Go driver.

Following are the things that our API achieves.
1. It reads a JSON object to add a document in a MongoDB collection.
2. It reads a document from MongoDB collection and display in JSON format.
3. It updates a document in a MongoDB collection.
4. It deletes a document from MongoDB collection. 


Softwares / Tools

1. MongoDB v4.0.8
2. Go 1.11.2
3. Eclipse 2018-09 (4.9.0)
4. Postman v7.7.0
  

Steps 

  • Create a new Go Project in eclipse by clicking File -> New -> Go Project. Name the project as 'GoApp'. Sample project structure is given below.

GoApp Project Structure
GoApp Project Structure

    For readers information, I have configured my Eclipse with GoClipse plugin to work with Go projects. They can use their favorite IDEs.
    • Create a source folder inside this project. Name it as 'api'. This is the folder that will contain 2 Go files that we will create as part of this blog.
    • Our first Go file is student.go comprising Student info that we will store as document in MongoDB.
    package main

    type Student struct {
        Roll  string
        Name  string
        Mark  int
        Grade string
    }

    var student Student 


    Here we create a Go struct object 'Student' with various attributes like Roll, Name, Mark, Grade etc. Also we create a variable of this struct object as 'student'.
    • Next Go file is main.go that contains API code for our Go project. It comprises one method to connect to MongDB, four methods for our CRUD operations and a main method to start server to invoke the API.
    package main

    import (
        "context"
        "encoding/json"
        "github.com/gorilla/mux"
        "go.mongodb.org/mongo-driver/bson"
        "go.mongodb.org/mongo-driver/mongo"
        "go.mongodb.org/mongo-driver/mongo/options"
        "io/ioutil"
        "log"
        "net/http"
        "time"
    )

    var collection *mongo.Collection

    func connectDB() {
        client, err := mongo.NewClient(options.Client().ApplyURI("mongodb://localhost:27017"))
        if err != nil {
            log.Fatal(err)
        }
        err = client.Connect(context.Background())
        if err != nil {
            log.Fatal(err)
        }
        collection = client.Database("student").Collection("studentScores")
        log.Println("Connected to DB")
    }

    func AddStudent(w http.ResponseWriter, r *http.Request) {
        body, err := ioutil.ReadAll(r.Body)
        if err != nil {
            log.Printf("Error reading body: %v", err)
            http.Error(w, "Can't read body", http.StatusBadRequest)
        }
        json.Unmarshal([]byte(body), &student)
        result, err := collection.InsertOne(context.Background(), student)
        if err != nil {
            log.Printf("Add Error:: %v", err)
        } else {
            if result.InsertedID != nil {
                log.Println("Student data is inserted successfully")
            }
        }
    }

    func UpdateStudent(w http.ResponseWriter, r *http.Request) {
        params := mux.Vars(r)
        filter := bson.D{{"roll", params["roll"]}}
        update := bson.D{
            {"$inc", bson.D{
                {"mark", 10},
            }},
        }
        result, err := collection.UpdateOne(context.TODO(), filter, update)
        if err != nil {
            log.Printf("Update Error:: %v", err)
        } else {
            if result.ModifiedCount == 1 {
                log.Println("Student data is updated successfully")
            }
        }
    }

    func DeleteStudent(w http.ResponseWriter, r *http.Request) {
        params := mux.Vars(r)
        filter := bson.D{{"roll", params["roll"]}}
        result, err := collection.DeleteOne(context.TODO(), filter)
        if err != nil {
            log.Printf("Delete Error:: %v", err)
        } else {
            if result.DeletedCount == 1 {
                log.Println("Student data is deleted successfully")
            }
        }
    }

    func GetStudent(w http.ResponseWriter, r *http.Request) {
        params := mux.Vars(r)
        filter := bson.D{{"roll", params["roll"]}}
        err := collection.FindOne(context.TODO(), filter).Decode(&student)
        if err != nil {
            log.Printf("Get Error:: %v", err)
        } else {
            log.Println("Student data is found")
            data, _ := json.Marshal(student)
            w.Header().Set("Content-Type", "application/json; charset=UTF-8")
            w.Header().Set("Access-Control-Allow-Origin", "*")
            w.WriteHeader(http.StatusOK)
            w.Write(data)
        }
    }

    func main() {
        connectDB()
        router := mux.NewRouter().StrictSlash(true)
        router.HandleFunc("/student/add", AddStudent).Methods("POST")
        router.HandleFunc("/student/get/{roll}", GetStudent).Methods("GET")
        router.HandleFunc("/student/update/{roll}", UpdateStudent).Methods("POST")
        router.HandleFunc("/student/delete/{roll}", DeleteStudent).Methods("POST")

        log.Println("Starting server on port 9100...")
        srv := &http.Server{
            Handler:      router,
            Addr:         "localhost:9100",
            WriteTimeout: 60 * time.Second,
            ReadTimeout:  60 * time.Second,
        }
        log.Fatal(srv.ListenAndServe())
    }

     

    Let me explain the methods briefly:
    1. Using connectDB(), we create a MongoDB client and through this client, we connect to our student database. Collection name is 'studentScores'.
    2. Through  AddStudent(), we read the JSON string in request body, unmarshal it and decode it into a student object. Then we add that student document into MongoDB collection.
    3. In UpdateStudent(), we read 'roll' parameter from the request, find the document from the collection and increase its 'mark' by 10. 
    4. In DeleteStudent(), we read 'roll' parameter from the request, search the document and delete it from the collection.
    5. Through GetStudent(), we read 'roll' parameter from the request and find the document. Then we marshal and display it in JSON format in response.
    6. In main() method, we do the mapping between requests and handlers using popular Go package 'gorilla/mux'. Then we create a HTTP server and start it.
    •  Now let us build and run our GoApp application. It will display the console with logs 'Connected to DB' and 'Starting server on port 9100...' if everything goes ok. Ensure your MongoDB instance is running at this stage.
    • Start Postman now to invoke the APIs. Below table shows various requests and responses.
    URL                                                     Request Body                                                   Response Body    -------------                                            ------------------                                                   --------------
    1. http://localhost:9100/student/add {"roll":"003", "name":"Sachin", "mark":1000, "grade":"B"} Nil
    2. http://localhost:9100/student/get/003 Nil { "Roll": "003", "Name": "Sachin","Mark": 1000,"grade": "B"}
    3. http://localhost:9100/student/update/003 Nil Nil
    4. http://localhost:9100/student/delete/003 Nil Nil

    Following is a sample Postman console for GetStudent API. User can call other APIs in the same order as mentioned in above table and do the CRUD operations.

    Request/Response for GetStudent API
    Request/Response for GetStudent API

    That's it for today. This is the first non Java blog of my blogging career. Hope readers will like it as much as my other Java blogs. To clear any apprehension, I am still a big admirer of Java and looking forward to write more Java blogs.

      No comments:

      Post a Comment