Haste Golang Light Weight Web Framework

Sudeep Dasgupta
4 min readJan 18, 2020

Here are the tutorials for Golang Haste Framework

Before step into the framework, lets better understand what is Golang and why do we need a framework.

Golang 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.
Golang has routes inbuild where in other languages you may find this in some framework like for example in Nodejs you will not find inbuild routes, but in Express framework which is built over Nodejs has routes facilities.
In Golang even we have template engine for rendering HTML and binding variables and loops over it, which again you won’t find in other languages directly you might have to use blade template, or jade template etc. Then if everything is inbuild in Golang why do we need a framework, well basically everyone does not have the same standard code of writing. We might need to develop middlewares and even some extra concurrency to do the task faster.

Here in Haste framework, we have inbuild middlewares, which you might need to validate session for example, in every request you receive. In this framework, we have used goroutines and even context module using channels to do the task faster and send the message from one module to other using channels. Future scope of this framework, we will have ODM for MongoDB and other NoSQL databases, ORM for SQL databases, inbuild connectivity with HDFS, MongoDB, SQL, Cassandra, Redis etc.

We have on more feature in this framework is the term we call projection. Suppose you have 5 modules in UI and need to call 5 REST APIs for that to get the message and then populate in the UI, rather than calling 5 APIs separately we will send 1 request with 5 method name and request inside the payload for example.

{
“Login”:{
“username”:”sudeep.dasgupta”,
“password”:”kaihiwatari”
},
“GetProfile”:{},
“SchemesMaster”:{
“category”:”EQUITY”
}
}

This payload has three methods one is login, the second one is GetProfile and last is SchemeMaster now rather than calling three Apis a single request was send along with the method name and all the three response is received. Now this method a drawback, it currently only supports JSON payload along with content-type: application/json.

One more thing about this projection is that you can send a response using http/1.1 but it will send response only after all the responses are prepared in the backend. But if you use WebSocket or http/2 streams then it will send the message only when the single method response is prepared and will eventually it will push all other after their message is prepared one by one.
This projection is actually inspired by Google GRPC, apache thrift and GraphQL.
To get the complete source code visit: https://github.com/pounze/go_haste

Bellow are the small snippet of creating routes middlewares and projection.

  1. Server.go page for initializing webserver
package main

import(
"cns"
"Web"
"net/http"
"fmt"
)

func main(){

httpApp := cns.Http{}

Web.Routes()

go func(){
defer cns.CreateHttpServer(":8000")
}()

defer cns.CreateHttpStreaming(":8100", "./ssl/https-server.crt","./ssl/https-server.key", "/streaming")

// we can also create https server

defer cns.CreateHttpsServer(":8300", "./ssl/https-server.crt","./ssl/https-server.key", "/")

// for http/2 server we write the following code

defer cns.CreateHttp2Server(":8300", "./ssl/https-server.crt","./ssl/https-server.key", "/")

defer httpApp.DefaultMethod(func(req *http.Request,res http.ResponseWriter){
res.Header().Set("Name", "Sudeep Dasgupta")
res.Header().Set("Content-Type", "application/json")
res.Header().Set("Access-Control-Allow-Origin", "*")
res.Header().Set("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept")
fmt.Println("Default header executed")
})
}

Here we have created 2 web servers 1 in http/1.1 and one in Http Streaming for projection. Then we have called a DefaultMethod which will be triggered whenever a new request is received. It can be used to set CORRS headers etc.

We write all routes inside the Web/RouteList.go

import(
"cns"
"fmt"
"net/http"
)

func Routes(){
httpApp := cns.Http{}

// for projection api create schema

httpApp.CreateSchema(map[string]cns.Projection{
"Login": cns.Projection{
sample.Login,
10,
},
"GetProfile": cns.Projection{
sample.GetProfile,
10,
},
});

// set route path for projection schema add middleware method to it

httpApp.SetRoutePath("/").Middlewares(func(req *http.Request,res http.ResponseWriter,done chan bool){
fmt.Println("middleware worked")
done <- true
})

// projection api for socket support

httpApp.SetSocketRoutePath("/realtime").Middlewares(func(req *http.Request,res http.ResponseWriter,done chan bool){
fmt.Println("middleware worked")
done <- true
})

// block directories to get access

httpApp.BlockDirectories([]string{"/UserView/img/","/UserView/js/"})

hm := map[string]string{
"$id":"[0-9]{2}",
"$name":"[a-z]+",
}

// to save file in case of multipart form data

httpApp.SaveFile(w http.ResponseWriter, file multipart.File, path string, fileChan chan bool)

// url matching using regular expression and calling multiple middleware with chaining

httpApp.Post("/$id/$name[\\/]*",func(req *http.Request,res http.ResponseWriter){
fmt.Println("method invoked")
fmt.Println(req.URL.Query().Get("$name"))
fmt.Fprintf(res, "Successfully Done")
}).Middlewares(func(req *http.Request,res http.ResponseWriter,done chan bool){

err := req.ParseMultipartForm(200000)
if err != nil {
fmt.Println("Unable to parse form data")
return
}
_,handle,_ := req.FormFile("name")
fmt.Println(handle)

done <- true
}).Middlewares(func(req *http.Request,res http.ResponseWriter,done chan bool){
fmt.Println("working both")
done <- true
}).Where(hm)

// calling Get request method same POST, PUT and DELETE available

httpApp.Get("/",func(req *http.Request,res http.ResponseWriter){
//cns.Push(res,"/UserView/js/test.js")
stat,_,result := cns.Authorization(req,res,"Enter username and password to Authenticate")
if stat{
fmt.Println(result)
http.ServeFile(res, req, "src/views/index.html")
}else{
fmt.Fprintf(res, "Unauthorized")
}
})

// TO call global middleware

httpApp.GlobalMiddleWares(func(req *http.Request,res http.ResponseWriter,done chan bool){
fmt.Println("working both")
done <- true
})

// creating try catch block to handle exception

cns.Block{
Try:func(){
fmt.Println("I tried")
cns.Throw("ohhh")
fmt.Println("Working")
},
Catch:func(e cns.Exception){
fmt.Println("caught exception",e)
},
Finally:func(){
fmt.Println("Finally")
},
}.Do()
}

For the Config.go, it is inside the cns/Config.go
There are lots of framework like gin, buffalo but you can benchmark this Haste with them and you may love it as its simple, fast and lightweight. We will try to improve the documentation better. Till then happy coding.

--

--

Sudeep Dasgupta

Machine Learning | Big Data | Video Streaming | Real-Time Low Latency Apps | Product Designer | Programmer | Open Source Contributor