Keep “Go”ing

Mrinmoy Das
9 min readJul 13, 2020

The road of a Solution Architect is never that easy. It requires keeping a constant check on the latest trends in technology, exploring them and suggesting how this new change is better or worse to the needs of the system you’re building. Sticking to this path, I decided to explore one of the most often heard about programming language in the world of technology — GoLang!

A quick Google search will define GoLang as -

Go is a statically typed, compiled programming language designed at Google by Robert Griesemer, Rob Pike, and Ken Thompson. Go is syntactically similar to C, but with memory safety, garbage collection, structural typing, and CSP-style concurrency.

Before I dig a little more deep into the topic, a basic introduction first — my name is Mrinmoy Das. I am an Architect working in an organisation BDI Plus. My domain of expertise is Digital Insurance, and we are currently building an API Gateway to provide our carriers and distributors a flawless experience integrating with us. As one do realise, that the foremost need of a successful gateway is reliability and consistency. A customer may not prefer waiting for more than a few seconds before they lose their patience and choose to walk away. While understanding the behaviours of our customers, the response time of our codebase needed to be in milliseconds (or even less). Reliability can only persist when the application holds hand with the system making it realise it’s need during runtime and for the system to react to it. This was one of the many reason, I decided to get my hands dirty with a bit of GoLang. I do code in Node, Spring Boot, Python Flask and do have the perquisites of the flow.

Before I am start posting snippets, I want to state that I am using go1.13.1 darwin/amd64. Running snippets present all over the internet appeared to be bit of an easy task. To make things difficult, I decided to write a RESTful API service which makes a connection to a MySQL database and queries it to read and write data into it. I decided to with Gin Web Framework — which is an open-source framework written in Go. It feautres a martini-like API with performance that is up to 40 times faster than the traditional httprouter (no kidding). Leaving a link below for you guys to check it out.

gin-gonic/gin

Although I did find a lot of articles and snippets of Go APIs over the internet, I wanted to create something from scratch. To understand and grasp the basics of Go it took me 4 whole days to create this service (I prefer writing everything down instead of copy-paste). But after spending this amount of time inhaling the essence of Go, I can say this is one of the most versatile language out there. The compiler is small and fast and do not hog on the system it is running on. Unlike Python, a datatype (struct) in Go is a tightly bounded component — one cannot simply declare a variable and use it as a object or even an array. Definitions needs to the precise. Error handling in one of the major factors working with Go. From defining a simple query to executing it, every step needs a try-catch block to prevent the application going haywire. And the most noticeable part of it is (at-least to me) is you cannot simply cover your entire screen with imports that you hardly even use in your code. If it is defined and not used, the compilation does fail.

Performance

Coming to some statistics. Problems like Mandelbrot, N-Body and Fasta were solved using Python and Go, and here is the result from running them. All units are in seconds.

Please note : all the tests where conducted in the same machine.

Scalability

While Python is based on Parallelism, GoLang is based on concurrency. The basic difference is that, concurrent sessions will not wait for the previous session to complete before starting of the new one, while parallel processing means running the same application instances at the same time. It is clear that concurrent application with work better when it comes to scalability.

Application

GoLang, although it was made public in 2009, has found it’s way into System Programming. With the advent of clustering and multi-node system, GoLang places a crucial role too. It is said, that using GoLang a server can be brought to life within a matter of seconds.

Execution

GoLang is a statically typed, compiler based programming language. As you will see in my code, every variable needs to be declared with it’s related data type during compilation itself.

Let’s begin with the service I did create. Please keep into consideration, I am still working on the project and can be made better in many ways that even I can think of. Will keep everyone updated with the link to my GitHub repositories.

The code begins with defining a package -

package main

Next, we need to import the libraries we want to use. The think that needs to be noted here is, one cannot declare as many imports as they want to and never use it again in the codebase. Only importing required and used libraries are allowed in Go.

import (
"database/sql"
"net/http"
"log"
"fmt"

"github.com/gin-gonic/gin"
_ "github.com/go-sql-driver/mysql"
)

As you can see, I have imported sql to make connection to the MySQL DB, a http library to return the status of the requests, log for logging, fmt or format for formatting, gin — the framework I will use, and a mysql driver.

Coming to the most important part of Go. As stated, Go is a well structured language and hence, any data type you want to declare needs to be defined with the exact data type. Here, I have created a definition of a datatype (struct) called User.

type User struct {
ID int64 `db:"id" json:"id"`
FirstName string `db:"firstname" json:"firstname"`
LastName string `db:"lastname" json:"lastname"`
}

Note : the mention of db and json parameters helps me use the same datatype both while accepting data into my service as JSON as well as mapping the same variable to the column name of the database table.

I have created a constant containing the details about the database connection

const (
DB_DETAILS = "root:password@tcp(127.0.0.1:3306)/Test?charset=utf8"
)

For people wondering, here is a snippet of the schema I am using to create this service. Will keep it as simple as possible.

CREATE TABLE `User` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`firstname` varchar(255) DEFAULT NULL,
`lastname` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=13 DEFAULT CHARSET=utf8

The execution of every Go program starts with the main function. Here I have used gin to define the routing of the APIs I will be defining.

func main() {

r := gin.Default()

v1 := r.Group("api/v1")
{
v1.POST("/users", PostUser)
}

r.Run(":8080")
}

My program runs on port 8080 with the route /api/v1/users. This is a POST call which will accept first name and last name of a user and store it in the database. I will not accept ID as it is an auto-increment value. If you do chose to do that even you can easily do so. The following snippets shows exactly how values are read from a JSON body.

I love write smaller piece of code. I liking dividing the code into as smaller chucks as possible for easy understanding. Here, I will define 2 functions — 1. To create the connection to the database. 2. To run the query and insert data into the database.

// The following 2 functions is to insert a row of data into the database
func PostUser(c *gin.Context) {

var user User
c.Bind(&user)

db, err := sql.Open("mysql", DB_DETAILS)
if err != nil {
log.Fatal("Connection creation failed!")
}
defer db.Close()

id, err := insert(db, user.FirstName, user.LastName) // Will be trying to pass this as a single parameter
if err != nil {
log.Fatal("Insert operation failed!")
}
log.Printf("Insertion completed with ID: %d\\n", id) // Learn all the different functions for logging/printing

c.JSON(http.StatusOK, gin.H{"message": "Insertion Completed!"})
}

func insert(db *sql.DB, FirstNameIn string, LastNameIn string) (int64, error) {

log.Printf(FirstNameIn, LastNameIn)

stmt, err := db.Prepare("INSERT INTO User (firstname, lastname) values (?, ?)")
if err != nil {
log.Fatal("Error in SQL syntax!")
return -1, err
}
defer stmt.Close()

res, err := stmt.Exec(FirstNameIn, LastNameIn)
if err != nil {
log.Fatal("Error in insert operation!")
return -1, err
}

return res.LastInsertId()

}

To run the above code, simply run — go run main.go (my go program file is called main and the extension is go)

Use the following curl to test your API -

curl --location --request POST '<http://127.0.0.1:8080/api/v1/users>' \\
--header 'Content-Type: application/json' \\
--data-raw '{
"firstname": "Mrinmoy",
"das": "Das"
}'

The time I took to understand this simple piece of code was to bind the incoming dataset with the datatype declared. I am attaching the entire piece of code below where I query the entire table and return it as a JSON. Do try it out yourself.

You will see a number of comments in my piece of code. Those are for me for better understanding. I am still working making the code as robust as possible. I understand the gaps of the codebase and I will keep on updating the piece till perfection.

package main

import (
"database/sql"
"net/http"
"log"
"fmt"
// "strconv"
// "encoding/json"

"github.com/gin-gonic/gin"
_ "github.com/go-sql-driver/mysql"
)

type User struct {
ID int64 `db:"id" json:"id"`
FirstName string `db:"firstname" json:"firstname"`
LastName string `db:"lastname" json:"lastname"`
}

const (
DB_DETAILS = "root:password@tcp(127.0.0.1:3306)/Test?charset=utf8"
)

func getDBConnection() {

// The idea is to move the connection to MySQL to this function
// so that I can use the same function over and over again for
// pooling

}

func main() {

r := gin.Default()

v1 := r.Group("api/v1")
{
v1.GET("/users", GetUsers)
v1.POST("/getuser", GetUser)
v1.POST("/users", PostUser)
}

r.Run(":8080")
}

// The following 2 functions is to get a single row of data as per the ID
// Note : If picking up from the path itself, use c.Params.ByName("id")
// I am going to define this as a POST call

func GetUser(c *gin.Context) {

var user User
c.Bind(&user)

db, err := sql.Open("mysql", DB_DETAILS)
if err != nil {
log.Fatal("Connection creation failed!")
}
defer db.Close()

res, err := getSingleUser(db, user.ID)
if err != nil {
log.Fatal("Select operation failed")
}
log.Println(res)
c.JSON(http.StatusOK, res)

}

func getSingleUser(db *sql.DB, IDin int64) (User, error) {

log.Println(IDin)

var user User
err := db.QueryRow("SELECT * FROM User WHERE id = ?", IDin).Scan(&user.ID, &user.FirstName, &user.LastName)
if err != nil {
log.Fatal("Error in select query")
return user, err
}

fmt.Println(user)

content := User {
ID : user.ID,
FirstName : user.FirstName,
LastName : user.LastName,
}

return content, err

}


// The following 2 functions is to fetch all the data from the database
func GetUsers(c *gin.Context) {

db, err := sql.Open("mysql", DB_DETAILS)

if err != nil {
log.Fatal("Connection creation failed!")
}
defer db.Close()

res, err := getALL(db)
if err != nil {
log.Fatal("Select operation failed!")
}
c.JSON(http.StatusOK, res)

}

func getALL(db *sql.DB) ([]User, error) {

var (
id int64
firstname string
lastname string
jsonData []User
)

res, err := db.Query("SELECT * FROM User")
if err != nil {
log.Fatal("Error in select statement")
return jsonData, err
}
defer res.Close()

for res.Next() {

err := res.Scan(&id, &firstname, &lastname)
if err != nil {
log.Fatal(err)
}
log.Println(id, firstname, lastname)

each := User {
ID: id,
FirstName: firstname,
LastName: lastname,
}

fmt.Println(each)

jsonData = append(jsonData, each)

}

return jsonData, err

}

// The following 2 functions is to insert a row of data into the database
func PostUser(c *gin.Context) {

var user User
c.Bind(&user)

db, err := sql.Open("mysql", DB_DETAILS)
if err != nil {
log.Fatal("Connection creation failed!")
}
defer db.Close()

id, err := insert(db, user.ID, user.FirstName, user.LastName) // Will be trying to pass this as a single parameter
if err != nil {
log.Fatal("Insert operation failed!")
}
log.Printf("Insertion completed with ID: %d\\n", id) // Learn all the different functions for logging/printing

c.JSON(http.StatusOK, gin.H{"message": "Insertion Completed!"})
}

func insert(db *sql.DB, IDin int64, FirstNameIn string, LastNameIn string) (int64, error) {

log.Printf(FirstNameIn, LastNameIn)

stmt, err := db.Prepare("INSERT INTO User (firstname, lastname) values (?, ?)")
if err != nil {
log.Fatal("Error in SQL syntax!")
return -1, err
}
defer stmt.Close()

res, err := stmt.Exec(FirstNameIn, LastNameIn)
if err != nil {
log.Fatal("Error in insert operation!")
return -1, err
}

return res.LastInsertId()

}

Take a look into the looping of the array while fetching all data from the table and using the same datatype User to map the object.

There is a learning curve to it, however, keeping in mind the performance, it’s worth taking the risk!

If you did like the post, please drop a few claps!

Again, I am updating all my handles across all social platform. You can find me at most places using the username — aemdeei

Collaborate with me on Notion -

https://www.notion.so/aemdeei/Keep-Go-ing-48af1d737a7c4c91bbcb4c7ca0aed181

Here is the best places for learning the hooks and nooks of Go -

The Go Programming Language

--

--