Sunday, April 16, 2017

REST API Using GO

Recently I have been working on developing strategy/vision for products. Micro Services based architecture is a clear winner in developing any new strategy for the reasons which are quite obvious. The objective of this blog is not to explain advantages of Micro Services based architecture but explore a programming vision which can last next decade.

I have been working with Java for 18 years. Obviously, I started thinking about Java while developing this vision. In Java world, in order to build a simple REST API, I need at least the following:
  •         Webserver - Tomcat or JBoss or Weblogic etc
  •        Jersey (for implementing REST API)
  •        Java 
  •        Other dependent jars

I started asking myself a simple question, why one has to learn WebServer, Jersey etc to write an API. I can obviously use Spring Boot which can help me abstract above mentioned dependencies to a certain extent, but still, why???

After a while, I told myself to take a few steps back and start fresh. Throw all the biasness towards Java. Flush out all the Martin Fowler blogs thatI learnt over the years J … etc etc …

As a developer, I wish to write APIs in an easy mechanism with fewer dependencies on the eco system surrounding it.  I started looking into Google Go programing language. I found this language easy to learn. It’s a small and self-contained language that runs really fast.

In this blog, I will explain, how to write REST APIs using GO.  Let’s assume we wish to write Person API, that performs the following:
  1. Get all persons
  2. Get person by id
  3. Create a person
  4. Delete a person

Person APIs in Go is essentially is a four step process, as shown pictorially below:
STEP 1: Define Data (Structures)

In GO a person (APerson) with an address (AnAddress) can be modelled as shown below:

type APerson struct {

       ID        string   `json:"id,omitempty"`

       Firstname string   `json:"firstname,omitempty"`

       Lastname  string   `json:"lastname,omitempty"`

       AnAddress  *AnAddress `json:"address,omitempty"`

}

type AnAddress struct {
       City  string `json:"city,omitempty"`
       State string `json:"state,omitempty"`
}

NOTE: Go has in built support for Json, as a result we can directly bind modelled data element to Json type.
NOTE: For the sake of simplicty, I have not included DB interactions in this blog.

STEP 2: APIs Definition

1) GetAllPersons – Implementation for API which fetches all the persons

func GetAllPersons(w http.ResponseWriter, req *http.Request) {
       json.NewEncoder(w).Encode(persons)
}

2) GetAPersonById – Implementation of API that fetches a person by Id

func GetAPersonById(w http.ResponseWriter, req *http.Request) {
       params := mux.Vars(req)
       for _, item := range persons {
              if item.ID == params["id"] {
                     json.NewEncoder(w).Encode(item)
                     return
              }
       }
       json.NewEncoder(w).Encode(&APerson{})
}

3) CreateAPerson – Implementation of API that creates a person

func CreateAPerson(w http.ResponseWriter, req *http.Request) {
       params := mux.Vars(req)
       var aPerson APerson   _ = json.NewDecoder(req.Body).Decode(&aPerson)
       aPerson.ID = params["id"]
       persons = append(persons, aPerson)
       json.NewEncoder(w).Encode(aPerson)
}

4) DeleteAPerson  - Implementation of API that deletes a person

func DeleteAPerson(w http.ResponseWriter, req *http.Request) {
       params := mux.Vars(req)
       for index, item := range persons {
              if item.ID == params["id"] {
                     persons = append(persons[:index], persons[index+1:]...)
                     break
              }
       }
       json.NewEncoder(w).Encode(persons)
}

STEP 3: Create a Router

A router registers routes to be dispatched to a matched function

  router := mux.NewRouter()
  router.HandleFunc("/persons", GetAPerson).Methods("GET")
 router.HandleFunc("/persons/{id}", GetAPersonById).Methods("GET")
  router.HandleFunc("/persons/{id}", CreateAPerson).Methods("POST")
   router.HandleFunc("/persons/{id}", DeleteAPerson).Methods("DELETE")

STEP 4: HTTP Listen and Serve

Start a HTTP server listening at port 8080 to handle all the requests/routes defined above.

http.ListenAndServe(":8080", router)
 
[
  {
    "id": "1",
    "firstname": "Gaurav",
    "lastname": "Malhotra",
    "address": {
      "city": "Utrecht",
      "state": "NL"
    }
  },
  {
    "id": "2",
    "firstname": "Shikha",
    "lastname": "Malhotra"
  }
]


Summary – Complexity to Simplicity

Go language provides very simple mechanism to write REST API with least dependencies. As a developer I can focus on developing my APIs and I don’t have to learn dependencies ecosystem around it. GO directly produce machine language, hence runs extremely fast. After writing my first API using GO, I see GO has bright future with ever so fast growing open source community. It can help in developing Micro Service based architecture easily. Also GO has built in garbage collection which makes it easy to manage memory. Last but not the least, GO has powerful support for concurrent execution (goroutines). Last but not the least, with least dependencies, it's very easy to write unit test & integration tests. It's worth a look!!! 

No comments: