Microservices in Go

Hey everyone, a huge welcome back to our blog! In this post, we will discuss about the what and how’s of microservices and implement a simple REST API in GoLang. So, what’re we waiting for? Let’s get started.

See the source image
let’s start

Microservices

Before writing some, let’s know what do we mean by Microservices. It’s an architectural style of building software, using few decoupled (loosely coupled) services. It helps us get job done in a more sophisticated way.

See the source image
Monolithic applications vs Microservices

In much understandable terms, it’s like dividing our logic into multiple functions and using them, instead of writing a whole sequential code. You should get it now 😉.

REST API

Are API’s and microservices the same? Well, The answer would be a big Noooo!

See the source image

Microservices is an architectural style, whereas REST API is an interface for users to access underlying procedures or functions.

So, a group of API’s can form a Microservice architecture, as we say dividing monolithic applications into multiple services, to decouple them from each other.

Microservices in GoLang

Go, a programming language introduced by Google more than a decade ago, known for its concurrency and wide architectural support is one of the best programming languages to write microservices in. Let’s Go 🙂

See the source image
Go Programming Language

Before, writing API’s let’s create a Go project. Make sure you install Go from their official website. After installation is done, create an empty directory and open your favorite command prompt at the same location. Type in the following command to initialize a new Go module.

go mod init github.com/HelloWorld/goProductAPI

We will consider writing CRUD (Create, Read, Update and Delete) API’s for a product catalog on an e-commerce site and also focus on organizing your code in the best way possible. So the attributes of the product would be as follows.

  • ID
  • Name
  • Description
  • Price
  • isAvailable

So, let’s go ahead and create a structure in Go, which would help us capture a product with similar information. Create a directory called entity in the root of your project and create a file called product.go with the following code.

package entity

//Product defines a structure for an item in product catalog
type Product struct {
	ID          string  `json:"id"`
	Name        string  `json:"name"`
	Description string  `json:"description"`
	Price       float64 `json:"price"`
	IsAvailable bool    `json:"isAvailable"`
}

The json tags assigned to the struct field let’s the json Marshaller know that this field is found with this particular name in a json file.

Now that we have structure defined, let’s create some mock data before writing our API’s to access them. Create a folder called data in the root of your project and paste the following content in a file named data.json.

[
    {
        "id": "c98e6fe3-7986-11eb-9b46-98fa9b64e75b",
        "name": "iPhone 12",
        "description": "Base variant of iPhone 12",
        "price": 80000,
        "isAvailable": true
    },
    {
        "id": "e7f31219-7986-11eb-bd65-98fa9b64e75b",
        "name": "iPhone 12 Pro",
        "description": "Pro variant of iPhone 12",
        "price": 100000,
        "isAvailable": false
    },
    {
        "id": "f82fa5ec-7986-11eb-a8e3-98fa9b64e75b",
        "name": "iPhone 12 Pro Max",
        "description": "Pro max variant of iPhone 12",
        "price": 120000,
        "isAvailable": true
    }
]

Now that, we have data let’s go ahead and write API’s to access/modify the defined data. In this blog we will focus on Create and Read API’s of CRUD operations. We will continue using the same example in the upcoming blogs, to know more and more about Microservices including database connections, security, deployment etc.

READ and CREATE API

Create another directory called handlers in the root of your project and paste the following code in a file named handlers.go, in which specify the functionality of an HTTP handlers.

Add the following code to handlers.go. We will go through each of those functions once you’re ready with the code.

package handlers

import (
	"encoding/json"
	"io/ioutil"
	"net/http"
	"os"

	"github.com/HelloWorld/goProductAPI/entity"
)

// GetProductHandler is used to get data inside the products defined on our product catalog
func GetProductHandler() http.HandlerFunc {
	return func(rw http.ResponseWriter, r *http.Request) {
		// Read JSON file
		data, err := ioutil.ReadFile("./data/data.json")
		if err != nil {
			rw.WriteHeader(http.StatusInternalServerError)
			return
		}
		// Write the body with JSON data
		rw.Header().Add("content-type", "application/json")
		rw.WriteHeader(http.StatusFound)
		rw.Write(data)
	}
}

// CreateProductHandler is used to create a new product and add to our product store.
func CreateProductHandler() http.HandlerFunc {
	return func(rw http.ResponseWriter, r *http.Request) {
		// Read incoming JSON from request body
		data, err := ioutil.ReadAll(r.Body)
		// If no body is associated return with StatusBadRequest
		if err != nil {
			rw.WriteHeader(http.StatusBadRequest)
			return
		}
		// Check if data is proper JSON (data validation)
		var product entity.Product
		err = json.Unmarshal(data, &product)
		if err != nil {
			rw.WriteHeader(http.StatusExpectationFailed)
			rw.Write([]byte("Invalid Data Format"))
			return
		}
		// Load existing products and append the data to product list
		var products []entity.Product
		data, err = ioutil.ReadFile("./data/data.json")
		if err != nil {
			rw.WriteHeader(http.StatusInternalServerError)
			return
		}
		// Load our JSON file to memory using array of products
		err = json.Unmarshal(data, &products)
		if err != nil {
			rw.WriteHeader(http.StatusInternalServerError)
			return
		}
		// Add new Product to our list
		products = append(products, product)
		
		// Write Updated JSON file
		updatedData, err := json.Marshal(products)
		if err != nil {
			rw.WriteHeader(http.StatusInternalServerError)
			return
		}
		err = ioutil.WriteFile("./data/data.json", updatedData, os.ModePerm)
		if err != nil {
			rw.WriteHeader(http.StatusInternalServerError)
			return
		}

		// return after writing Body
		rw.WriteHeader(http.StatusCreated)
		rw.Write([]byte("Added New Product"))
	}
}

In the above code, there are two handlers which return a http.HandlerFunc type which will be used by us while creating an http server, to handle incoming requests on a specific route.

The http handler has two input parameters, one the http request variable with all the information coming in along with the http request, the other the response writer to which we write back responses.

We are using a static file for now to read all the data from, and to write data in case of POST request. I have added the comments in the code to make it self explanatory. Please go through them for better understanding on what we do in every line.

The main package

In Go, the entry point is the main function and that should be in the package main. So, go ahead and create a main.go file in the root of your project. Add the following code to the newly created file.

package main

import (
	"fmt"
	"net/http"

	"github.com/HelloWorld/goProductAPI/handlers"
	"github.com/gorilla/mux"
)

func main() {
	// Create new Router
	router := mux.NewRouter()

	// route properly to respective handlers
	router.Handle("/products", handlers.GetProductHandler()).Methods("GET")
	router.Handle("/products", handlers.CreateProductHandler()).Methods("POST")

	// Create new server and assign the router
	server := http.Server{
		Addr:    ":9090",
		Handler: router,
	}
	fmt.Println("Staring Product Catalog server on Port 9090")
	// Start Server on defined port/host.
	server.ListenAndServe()
}

In the above file, we are creating a new mux router, which helps us in routing http requests to respective handlers, as we have did in the following lines of code. Then, we have created a new HTTP server, which should run on 9090 port on your machine after running this main.go file. As always, go through the comments for better understanding.

Let’s test this

Open a new command prompt in your project location, and type in the following command.

go run main.go

We will test our API using Postman tool, which is widely used in API development and testing purposes. Here’s the link to the tool, if you haven’t downloaded yet.

Create a new request and make a call to http://localhost:9090/products. The response would look like this.

Postman response for GET API

Let’s also test the POST request by making post call on the same route. We will add the following JSON to the document.

{
   "id": "e7f35219-7986-11eb-bd65-98fa9b64e75b",
   "name": "iPhone 12 Mini",
   "description": "Mini variant of iPhone 12",
   "price": 60000,
   "isAvailable": true
}

Add the above JSON to the body of the outgoing POST request from postman.

POST request in postman

You should see the response as Added New Product with 201 Created as response header. Go back to VS code to check if this is added to our data.json in the data directory. Here’s mine.

VSCode json file

Great, you now know how to create a simple REST API using GoLang. You can find the whole code on our GitHub repository. We will explore more in our upcoming blogs.

In our next blog, we will add DELETE and PUT API to our product catalog server. There’re some changes required in GET and POST as well. We will see them too. Until then, Stay safe. Cheers ✌✌

Blogs in this Series

7 responses to “Microservices in Go”

  1. The prices are float64, yet in your JSON the prices are in quotations. This makes for problems when POSTing the iPhone Mini 12 to the JSON file. Remove the quotes around the prices.

    Like

Leave a reply to Jeffrey Cancel reply

Design a site like this with WordPress.com
Get started